diff options
| author | HybridChild <[email protected]> | 2025-11-12 21:03:36 +0100 |
|---|---|---|
| committer | HybridChild <[email protected]> | 2025-11-12 21:03:36 +0100 |
| commit | 18a43908725366a230c990b8dee4a1d89fef11d0 (patch) | |
| tree | f2c515ecc23131e1ef7a4bebc15f025e23a62f03 | |
| parent | 973fdb6b222a24e881c722b33767aab76ab92896 (diff) | |
| parent | 07c918023c1b233a2f9f16eb0b654169c0379f79 (diff) | |
Merge branch 'main' into stm32_i2c_v2_transaction
36 files changed, 1583 insertions, 1571 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0a1854dab..ec49924a2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -139,6 +139,7 @@ flavors = [ | |||
| 139 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, | 139 | { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, |
| 140 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, | 140 | { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, |
| 141 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, | 141 | { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, |
| 142 | { regex_feature = "stm32n6.*", target = "thumbv8m.main-none-eabihf" }, | ||
| 142 | ] | 143 | ] |
| 143 | 144 | ||
| 144 | [package.metadata.docs.rs] | 145 | [package.metadata.docs.rs] |
| @@ -198,11 +199,11 @@ aligned = "0.4.1" | |||
| 198 | heapless = "0.9.1" | 199 | heapless = "0.9.1" |
| 199 | 200 | ||
| 200 | #stm32-metapac = { version = "18" } | 201 | #stm32-metapac = { version = "18" } |
| 201 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e" } | 202 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2" } |
| 202 | 203 | ||
| 203 | [build-dependencies] | 204 | [build-dependencies] |
| 204 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 205 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 205 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-22374e3344a2c9150b9b3d4da45c03f398fdc54e", default-features = false, features = ["metadata"] } | 206 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2", default-features = false, features = ["metadata"] } |
| 206 | 207 | ||
| 207 | proc-macro2 = "1.0.36" | 208 | proc-macro2 = "1.0.36" |
| 208 | quote = "1.0.15" | 209 | quote = "1.0.15" |
| @@ -1642,7 +1643,30 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] | |||
| 1642 | stm32l562re = [ "stm32-metapac/stm32l562re" ] | 1643 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1643 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] | 1644 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1644 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] | 1645 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] |
| 1646 | stm32n645a0 = [ "stm32-metapac/stm32n645a0" ] | ||
| 1647 | stm32n645b0 = [ "stm32-metapac/stm32n645b0" ] | ||
| 1648 | stm32n645i0 = [ "stm32-metapac/stm32n645i0" ] | ||
| 1649 | stm32n645l0 = [ "stm32-metapac/stm32n645l0" ] | ||
| 1650 | stm32n645x0 = [ "stm32-metapac/stm32n645x0" ] | ||
| 1651 | stm32n645z0 = [ "stm32-metapac/stm32n645z0" ] | ||
| 1652 | stm32n647a0 = [ "stm32-metapac/stm32n647a0" ] | ||
| 1653 | stm32n647b0 = [ "stm32-metapac/stm32n647b0" ] | ||
| 1654 | stm32n647i0 = [ "stm32-metapac/stm32n647i0" ] | ||
| 1655 | stm32n647l0 = [ "stm32-metapac/stm32n647l0" ] | ||
| 1656 | stm32n647x0 = [ "stm32-metapac/stm32n647x0" ] | ||
| 1657 | stm32n647z0 = [ "stm32-metapac/stm32n647z0" ] | ||
| 1658 | stm32n655a0 = [ "stm32-metapac/stm32n655a0" ] | ||
| 1659 | stm32n655b0 = [ "stm32-metapac/stm32n655b0" ] | ||
| 1660 | stm32n655i0 = [ "stm32-metapac/stm32n655i0" ] | ||
| 1661 | stm32n655l0 = [ "stm32-metapac/stm32n655l0" ] | ||
| 1662 | stm32n655x0 = [ "stm32-metapac/stm32n655x0" ] | ||
| 1663 | stm32n655z0 = [ "stm32-metapac/stm32n655z0" ] | ||
| 1664 | stm32n657a0 = [ "stm32-metapac/stm32n657a0" ] | ||
| 1665 | stm32n657b0 = [ "stm32-metapac/stm32n657b0" ] | ||
| 1666 | stm32n657i0 = [ "stm32-metapac/stm32n657i0" ] | ||
| 1667 | stm32n657l0 = [ "stm32-metapac/stm32n657l0" ] | ||
| 1645 | stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] | 1668 | stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] |
| 1669 | stm32n657z0 = [ "stm32-metapac/stm32n657z0" ] | ||
| 1646 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] | 1670 | stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] |
| 1647 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] | 1671 | stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] |
| 1648 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] | 1672 | stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 09a05ce68..48da475df 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1353,6 +1353,8 @@ fn main() { | |||
| 1353 | 1353 | ||
| 1354 | for p in METADATA.peripherals { | 1354 | for p in METADATA.peripherals { |
| 1355 | if let Some(regs) = &p.registers { | 1355 | if let Some(regs) = &p.registers { |
| 1356 | let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new(); | ||
| 1357 | |||
| 1356 | for pin in p.pins { | 1358 | for pin in p.pins { |
| 1357 | let key = (regs.kind, pin.signal); | 1359 | let key = (regs.kind, pin.signal); |
| 1358 | if let Some(tr) = signals.get(&key) { | 1360 | if let Some(tr) = signals.get(&key) { |
| @@ -1474,25 +1476,29 @@ fn main() { | |||
| 1474 | }; | 1476 | }; |
| 1475 | 1477 | ||
| 1476 | // H7 has differential voltage measurements | 1478 | // H7 has differential voltage measurements |
| 1477 | let ch: Option<u8> = if pin.signal.starts_with("INP") { | 1479 | let ch: Option<(u8, bool)> = if pin.signal.starts_with("INP") { |
| 1478 | Some(pin.signal.strip_prefix("INP").unwrap().parse().unwrap()) | 1480 | Some((pin.signal.strip_prefix("INP").unwrap().parse().unwrap(), false)) |
| 1479 | } else if pin.signal.starts_with("INN") { | 1481 | } else if pin.signal.starts_with("INN") { |
| 1480 | // TODO handle in the future when embassy supports differential measurements | 1482 | Some((pin.signal.strip_prefix("INN").unwrap().parse().unwrap(), true)) |
| 1481 | None | ||
| 1482 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { | 1483 | } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') { |
| 1483 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 | 1484 | // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 |
| 1484 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); | 1485 | let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap(); |
| 1485 | Some(32u8 + signal.parse::<u8>().unwrap()) | 1486 | Some((32u8 + signal.parse::<u8>().unwrap(), false)) |
| 1486 | } else if pin.signal.starts_with("IN") { | 1487 | } else if pin.signal.starts_with("IN") { |
| 1487 | Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) | 1488 | Some((pin.signal.strip_prefix("IN").unwrap().parse().unwrap(), false)) |
| 1488 | } else { | 1489 | } else { |
| 1489 | None | 1490 | None |
| 1490 | }; | 1491 | }; |
| 1491 | if let Some(ch) = ch { | 1492 | if let Some((ch, false)) = ch { |
| 1493 | adc_pairs.entry(ch).or_insert((None, None)).0.replace(pin_name.clone()); | ||
| 1494 | |||
| 1492 | g.extend(quote! { | 1495 | g.extend(quote! { |
| 1493 | impl_adc_pin!( #peri, #pin_name, #ch); | 1496 | impl_adc_pin!( #peri, #pin_name, #ch); |
| 1494 | }) | 1497 | }) |
| 1495 | } | 1498 | } |
| 1499 | if let Some((ch, true)) = ch { | ||
| 1500 | adc_pairs.entry(ch).or_insert((None, None)).1.replace(pin_name.clone()); | ||
| 1501 | } | ||
| 1496 | } | 1502 | } |
| 1497 | 1503 | ||
| 1498 | if regs.kind == "opamp" { | 1504 | if regs.kind == "opamp" { |
| @@ -1531,6 +1537,23 @@ fn main() { | |||
| 1531 | }) | 1537 | }) |
| 1532 | } | 1538 | } |
| 1533 | } | 1539 | } |
| 1540 | |||
| 1541 | { | ||
| 1542 | let peri = format_ident!("{}", p.name); | ||
| 1543 | |||
| 1544 | for (ch, (pin, npin)) in adc_pairs { | ||
| 1545 | let (pin_name, npin_name) = match (pin, npin) { | ||
| 1546 | (Some(pin), Some(npin)) => (pin, npin), | ||
| 1547 | _ => { | ||
| 1548 | continue; | ||
| 1549 | } | ||
| 1550 | }; | ||
| 1551 | |||
| 1552 | g.extend(quote! { | ||
| 1553 | impl_adc_pair!( #peri, #pin_name, #npin_name, #ch); | ||
| 1554 | }) | ||
| 1555 | } | ||
| 1556 | } | ||
| 1534 | } | 1557 | } |
| 1535 | } | 1558 | } |
| 1536 | 1559 | ||
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..04d976513 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR | |||
| 4 | #[cfg(stm32wba)] | 4 | #[cfg(stm32wba)] |
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; |
| 6 | 6 | ||
| 7 | use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; | 7 | use super::{AdcChannel, AnyAdcChannel, RxDma4, blocking_delay_us}; |
| 8 | use crate::dma::Transfer; | 8 | use crate::dma::Transfer; |
| 9 | #[cfg(stm32u5)] | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; |
| @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 25 | pub const VREF_CALIB_MV: u32 = 3300; | 25 | pub const VREF_CALIB_MV: u32 = 3300; |
| 26 | 26 | ||
| 27 | const VREF_CHANNEL: u8 = 0; | 27 | impl<'d, T: Instance> super::SealedSpecialConverter<super::VrefInt> for Adc4<'d, T> { |
| 28 | const VCORE_CHANNEL: u8 = 12; | 28 | const CHANNEL: u8 = 0; |
| 29 | const TEMP_CHANNEL: u8 = 13; | ||
| 30 | const VBAT_CHANNEL: u8 = 14; | ||
| 31 | const DAC_CHANNEL: u8 = 21; | ||
| 32 | |||
| 33 | // 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 | ||
| 34 | /// Internal voltage reference channel. | ||
| 35 | pub struct VrefInt; | ||
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 37 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 38 | fn channel(&self) -> u8 { | ||
| 39 | VREF_CHANNEL | ||
| 40 | } | ||
| 41 | } | 29 | } |
| 42 | 30 | ||
| 43 | /// Internal temperature channel. | 31 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Temperature> for Adc4<'d, T> { |
| 44 | pub struct Temperature; | 32 | const CHANNEL: u8 = 13; |
| 45 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 46 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 47 | fn channel(&self) -> u8 { | ||
| 48 | TEMP_CHANNEL | ||
| 49 | } | ||
| 50 | } | 33 | } |
| 51 | 34 | ||
| 52 | /// Internal battery voltage channel. | 35 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vcore> for Adc4<'d, T> { |
| 53 | pub struct Vbat; | 36 | const CHANNEL: u8 = 12; |
| 54 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 55 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 56 | fn channel(&self) -> u8 { | ||
| 57 | VBAT_CHANNEL | ||
| 58 | } | ||
| 59 | } | 37 | } |
| 60 | 38 | ||
| 61 | /// Internal DAC channel. | 39 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vbat> for Adc4<'d, T> { |
| 62 | pub struct Dac; | 40 | const CHANNEL: u8 = 14; |
| 63 | impl<T: Instance> AdcChannel<T> for Dac {} | ||
| 64 | impl<T: Instance> SealedAdcChannel<T> for Dac { | ||
| 65 | fn channel(&self) -> u8 { | ||
| 66 | DAC_CHANNEL | ||
| 67 | } | ||
| 68 | } | 41 | } |
| 69 | 42 | ||
| 70 | /// Internal Vcore channel. | 43 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Dac> for Adc4<'d, T> { |
| 71 | pub struct Vcore; | 44 | const CHANNEL: u8 = 21; |
| 72 | impl<T: Instance> AdcChannel<T> for Vcore {} | ||
| 73 | impl<T: Instance> SealedAdcChannel<T> for Vcore { | ||
| 74 | fn channel(&self) -> u8 { | ||
| 75 | VCORE_CHANNEL | ||
| 76 | } | ||
| 77 | } | 45 | } |
| 78 | 46 | ||
| 79 | #[derive(Copy, Clone)] | 47 | #[derive(Copy, Clone)] |
| @@ -214,20 +182,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 214 | ); | 182 | ); |
| 215 | } | 183 | } |
| 216 | 184 | ||
| 217 | let mut s = Self { adc }; | ||
| 218 | |||
| 219 | s.power_up(); | ||
| 220 | |||
| 221 | s.calibrate(); | ||
| 222 | blocking_delay_us(1); | ||
| 223 | |||
| 224 | s.enable(); | ||
| 225 | s.configure(); | ||
| 226 | |||
| 227 | s | ||
| 228 | } | ||
| 229 | |||
| 230 | fn power_up(&mut self) { | ||
| 231 | T::regs().isr().modify(|w| { | 185 | T::regs().isr().modify(|w| { |
| 232 | w.set_ldordy(true); | 186 | w.set_ldordy(true); |
| 233 | }); | 187 | }); |
| @@ -239,22 +193,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 239 | T::regs().isr().modify(|w| { | 193 | T::regs().isr().modify(|w| { |
| 240 | w.set_ldordy(true); | 194 | w.set_ldordy(true); |
| 241 | }); | 195 | }); |
| 242 | } | ||
| 243 | 196 | ||
| 244 | fn calibrate(&mut self) { | ||
| 245 | T::regs().cr().modify(|w| w.set_adcal(true)); | 197 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 246 | while T::regs().cr().read().adcal() {} | 198 | while T::regs().cr().read().adcal() {} |
| 247 | T::regs().isr().modify(|w| w.set_eocal(true)); | 199 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 248 | } | ||
| 249 | 200 | ||
| 250 | fn enable(&mut self) { | 201 | blocking_delay_us(1); |
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | 202 | |
| 252 | T::regs().cr().modify(|w| w.set_aden(true)); | 203 | Self::enable(); |
| 253 | while !T::regs().isr().read().adrdy() {} | ||
| 254 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 255 | } | ||
| 256 | 204 | ||
| 257 | fn configure(&mut self) { | ||
| 258 | // single conversion mode, software trigger | 205 | // single conversion mode, software trigger |
| 259 | T::regs().cfgr1().modify(|w| { | 206 | T::regs().cfgr1().modify(|w| { |
| 260 | #[cfg(stm32u5)] | 207 | #[cfg(stm32u5)] |
| @@ -280,51 +227,60 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 280 | w.set_smpsel(i, Smpsel::SMP1); | 227 | w.set_smpsel(i, Smpsel::SMP1); |
| 281 | } | 228 | } |
| 282 | }); | 229 | }); |
| 230 | |||
| 231 | Self { adc } | ||
| 232 | } | ||
| 233 | |||
| 234 | fn enable() { | ||
| 235 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 236 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 237 | while !T::regs().isr().read().adrdy() {} | ||
| 238 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 283 | } | 239 | } |
| 284 | 240 | ||
| 285 | /// Enable reading the voltage reference internal channel. | 241 | /// Enable reading the voltage reference internal channel. |
| 286 | pub fn enable_vrefint(&self) -> VrefInt { | 242 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 287 | T::regs().ccr().modify(|w| { | 243 | T::regs().ccr().modify(|w| { |
| 288 | w.set_vrefen(true); | 244 | w.set_vrefen(true); |
| 289 | }); | 245 | }); |
| 290 | 246 | ||
| 291 | VrefInt {} | 247 | super::VrefInt {} |
| 292 | } | 248 | } |
| 293 | 249 | ||
| 294 | /// Enable reading the temperature internal channel. | 250 | /// Enable reading the temperature internal channel. |
| 295 | pub fn enable_temperature(&self) -> Temperature { | 251 | pub fn enable_temperature(&self) -> super::Temperature { |
| 296 | T::regs().ccr().modify(|w| { | 252 | T::regs().ccr().modify(|w| { |
| 297 | w.set_vsensesel(true); | 253 | w.set_vsensesel(true); |
| 298 | }); | 254 | }); |
| 299 | 255 | ||
| 300 | Temperature {} | 256 | super::Temperature {} |
| 301 | } | 257 | } |
| 302 | 258 | ||
| 303 | /// Enable reading the vbat internal channel. | 259 | /// Enable reading the vbat internal channel. |
| 304 | #[cfg(stm32u5)] | 260 | #[cfg(stm32u5)] |
| 305 | pub fn enable_vbat(&self) -> Vbat { | 261 | pub fn enable_vbat(&self) -> super::Vbat { |
| 306 | T::regs().ccr().modify(|w| { | 262 | T::regs().ccr().modify(|w| { |
| 307 | w.set_vbaten(true); | 263 | w.set_vbaten(true); |
| 308 | }); | 264 | }); |
| 309 | 265 | ||
| 310 | Vbat {} | 266 | super::Vbat {} |
| 311 | } | 267 | } |
| 312 | 268 | ||
| 313 | /// Enable reading the vbat internal channel. | 269 | /// Enable reading the vbat internal channel. |
| 314 | pub fn enable_vcore(&self) -> Vcore { | 270 | pub fn enable_vcore(&self) -> super::Vcore { |
| 315 | Vcore {} | 271 | super::Vcore {} |
| 316 | } | 272 | } |
| 317 | 273 | ||
| 318 | /// Enable reading the vbat internal channel. | 274 | /// Enable reading the vbat internal channel. |
| 319 | #[cfg(stm32u5)] | 275 | #[cfg(stm32u5)] |
| 320 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 276 | pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { |
| 321 | let mux; | 277 | let mux; |
| 322 | match dac { | 278 | match dac { |
| 323 | DacChannel::OUT1 => mux = false, | 279 | DacChannel::OUT1 => mux = false, |
| 324 | DacChannel::OUT2 => mux = true, | 280 | DacChannel::OUT2 => mux = true, |
| 325 | } | 281 | } |
| 326 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 282 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 327 | Dac {} | 283 | super::Dac {} |
| 328 | } | 284 | } |
| 329 | 285 | ||
| 330 | /// Set the ADC resolution. | 286 | /// Set the ADC resolution. |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 1869993a5..bc97a7c4b 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -24,11 +24,11 @@ const CHSELR_SQ_SIZE: usize = 8; | |||
| 24 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 24 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 25 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 25 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| 26 | 26 | ||
| 27 | impl<T: Instance> super::VrefConverter for T { | 27 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 28 | const CHANNEL: u8 = 10; | 28 | const CHANNEL: u8 = 10; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | impl<T: Instance> super::TemperatureConverter for T { | 31 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 32 | const CHANNEL: u8 = 9; | 32 | const CHANNEL: u8 = 9; |
| 33 | } | 33 | } |
| 34 | 34 | ||
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 835cc8c63..f6220de78 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -3,7 +3,7 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::interrupt::{self}; | 8 | use crate::interrupt::{self}; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| @@ -28,11 +28,11 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | impl<T: Instance> super::VrefConverter for T { | 31 | impl<T: Instance> super::SealedSpecialConverter<VrefInt> for T { |
| 32 | const CHANNEL: u8 = 17; | 32 | const CHANNEL: u8 = 17; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | impl<T: Instance> super::TemperatureConverter for T { | 35 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 36 | const CHANNEL: u8 = 16; | 36 | const CHANNEL: u8 = 16; |
| 37 | } | 37 | } |
| 38 | 38 | ||
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index f6a4e1209..4a77f3c5b 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -3,7 +3,7 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 9 | use crate::{Peri, interrupt, rcc}; | 9 | use crate::{Peri, interrupt, rcc}; |
| @@ -29,11 +29,11 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | impl<T: Instance> super::VrefConverter for T { | 32 | impl<T: Instance> super::SealedSpecialConverter<VrefInt> for T { |
| 33 | const CHANNEL: u8 = 18; | 33 | const CHANNEL: u8 = 18; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | impl<T: Instance> super::TemperatureConverter for T { | 36 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 37 | const CHANNEL: u8 = 16; | 37 | const CHANNEL: u8 = 16; |
| 38 | } | 38 | } |
| 39 | 39 | ||
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 5d9c6ff74..0a9f35a5b 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 3 | #[allow(unused)] | 1 | #[allow(unused)] |
| 4 | #[cfg(stm32h7)] | 2 | #[cfg(stm32h7)] |
| 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| @@ -10,15 +8,14 @@ pub use pac::adccommon::vals::Presc; | |||
| 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 8 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | 9 | pub use stm32_metapac::adccommon::vals::Dual; |
| 12 | 10 | ||
| 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | 11 | use super::{ |
| 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, | ||
| 13 | blocking_delay_us, | ||
| 14 | }; | ||
| 14 | use crate::adc::SealedAdcChannel; | 15 | use crate::adc::SealedAdcChannel; |
| 15 | use crate::dma::Transfer; | ||
| 16 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | 18 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | 19 | mod injected; |
| 23 | pub use injected::InjectedAdc; | 20 | pub use injected::InjectedAdc; |
| 24 | 21 | ||
| @@ -103,6 +100,19 @@ impl Prescaler { | |||
| 103 | } | 100 | } |
| 104 | } | 101 | } |
| 105 | 102 | ||
| 103 | /// ADC configuration | ||
| 104 | #[derive(Default)] | ||
| 105 | pub struct AdcConfig { | ||
| 106 | pub dual_mode: Option<Dual>, | ||
| 107 | pub resolution: Option<Resolution>, | ||
| 108 | #[cfg(stm32g4)] | ||
| 109 | pub oversampling_shift: Option<u8>, | ||
| 110 | #[cfg(stm32g4)] | ||
| 111 | pub oversampling_ratio: Option<u8>, | ||
| 112 | #[cfg(stm32g4)] | ||
| 113 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 114 | } | ||
| 115 | |||
| 106 | // Trigger source for ADC conversions¨ | 116 | // Trigger source for ADC conversions¨ |
| 107 | #[derive(Copy, Clone)] | 117 | #[derive(Copy, Clone)] |
| 108 | pub struct ConversionTrigger { | 118 | pub struct ConversionTrigger { |
| @@ -112,18 +122,9 @@ pub struct ConversionTrigger { | |||
| 112 | pub edge: Exten, | 122 | pub edge: Exten, |
| 113 | } | 123 | } |
| 114 | 124 | ||
| 115 | // Conversion mode for regular ADC channels | ||
| 116 | #[derive(Copy, Clone)] | ||
| 117 | pub enum RegularConversionMode { | ||
| 118 | // Samples as fast as possible | ||
| 119 | Continuous, | ||
| 120 | // Sample at rate determined by external trigger | ||
| 121 | Triggered(ConversionTrigger), | ||
| 122 | } | ||
| 123 | |||
| 124 | impl<'d, T: Instance> Adc<'d, T> { | 125 | impl<'d, T: Instance> Adc<'d, T> { |
| 125 | /// Create a new ADC driver. | 126 | /// Create a new ADC driver. |
| 126 | pub fn new(adc: Peri<'d, T>) -> Self { | 127 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 127 | rcc::enable_and_reset::<T>(); | 128 | rcc::enable_and_reset::<T>(); |
| 128 | 129 | ||
| 129 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 130 | let prescaler = Prescaler::from_ker_ck(T::frequency()); |
| @@ -140,37 +141,19 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 140 | ); | 141 | ); |
| 141 | } | 142 | } |
| 142 | 143 | ||
| 143 | let mut s = Self { adc }; | ||
| 144 | s.power_up(); | ||
| 145 | s.configure_differential_inputs(); | ||
| 146 | |||
| 147 | s.calibrate(); | ||
| 148 | blocking_delay_us(1); | ||
| 149 | |||
| 150 | Self::enable(); | ||
| 151 | s.configure(); | ||
| 152 | |||
| 153 | s | ||
| 154 | } | ||
| 155 | |||
| 156 | fn power_up(&mut self) { | ||
| 157 | T::regs().cr().modify(|reg| { | 144 | T::regs().cr().modify(|reg| { |
| 158 | reg.set_deeppwd(false); | 145 | reg.set_deeppwd(false); |
| 159 | reg.set_advregen(true); | 146 | reg.set_advregen(true); |
| 160 | }); | 147 | }); |
| 161 | 148 | ||
| 162 | blocking_delay_us(20); | 149 | blocking_delay_us(20); |
| 163 | } | ||
| 164 | 150 | ||
| 165 | fn configure_differential_inputs(&mut self) { | ||
| 166 | T::regs().difsel().modify(|w| { | 151 | T::regs().difsel().modify(|w| { |
| 167 | for n in 0..18 { | 152 | for n in 0..18 { |
| 168 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 153 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 169 | } | 154 | } |
| 170 | }); | 155 | }); |
| 171 | } | ||
| 172 | 156 | ||
| 173 | fn calibrate(&mut self) { | ||
| 174 | T::regs().cr().modify(|w| { | 157 | T::regs().cr().modify(|w| { |
| 175 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 158 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| 176 | }); | 159 | }); |
| @@ -190,9 +173,47 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 190 | while T::regs().cr().read().adcal() {} | 173 | while T::regs().cr().read().adcal() {} |
| 191 | 174 | ||
| 192 | blocking_delay_us(20); | 175 | blocking_delay_us(20); |
| 176 | |||
| 177 | Self::enable(); | ||
| 178 | |||
| 179 | // single conversion mode, software trigger | ||
| 180 | T::regs().cfgr().modify(|w| { | ||
| 181 | w.set_cont(false); | ||
| 182 | w.set_exten(Exten::DISABLED); | ||
| 183 | }); | ||
| 184 | |||
| 185 | if let Some(dual) = config.dual_mode { | ||
| 186 | T::common_regs().ccr().modify(|reg| { | ||
| 187 | reg.set_dual(dual); | ||
| 188 | }) | ||
| 189 | } | ||
| 190 | |||
| 191 | if let Some(resolution) = config.resolution { | ||
| 192 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 193 | } | ||
| 194 | |||
| 195 | #[cfg(stm32g4)] | ||
| 196 | if let Some(shift) = config.oversampling_shift { | ||
| 197 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(stm32g4)] | ||
| 201 | if let Some(ratio) = config.oversampling_ratio { | ||
| 202 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 203 | } | ||
| 204 | |||
| 205 | #[cfg(stm32g4)] | ||
| 206 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 207 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 208 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 209 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 210 | } | ||
| 211 | |||
| 212 | Self { adc } | ||
| 193 | } | 213 | } |
| 194 | 214 | ||
| 195 | fn enable() { | 215 | /// Enable the ADC |
| 216 | pub(super) fn enable() { | ||
| 196 | // Make sure bits are off | 217 | // Make sure bits are off |
| 197 | while T::regs().cr().read().addis() { | 218 | while T::regs().cr().read().addis() { |
| 198 | // spin | 219 | // spin |
| @@ -213,119 +234,33 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 213 | } | 234 | } |
| 214 | } | 235 | } |
| 215 | 236 | ||
| 216 | fn configure(&mut self) { | 237 | /// Start regular adc conversion |
| 217 | // single conversion mode, software trigger | 238 | pub(super) fn start() { |
| 218 | T::regs().cfgr().modify(|w| { | 239 | T::regs().cr().modify(|reg| { |
| 219 | w.set_cont(false); | 240 | reg.set_adstart(true); |
| 220 | w.set_exten(Exten::DISABLED); | ||
| 221 | }); | ||
| 222 | } | ||
| 223 | |||
| 224 | /// Enable reading the voltage reference internal channel. | ||
| 225 | pub fn enable_vrefint(&self) -> super::VrefInt | ||
| 226 | where | ||
| 227 | T: super::VrefConverter, | ||
| 228 | { | ||
| 229 | T::common_regs().ccr().modify(|reg| { | ||
| 230 | reg.set_vrefen(true); | ||
| 231 | }); | ||
| 232 | |||
| 233 | super::VrefInt {} | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Enable reading the temperature internal channel. | ||
| 237 | pub fn enable_temperature(&self) -> super::Temperature | ||
| 238 | where | ||
| 239 | T: super::TemperatureConverter, | ||
| 240 | { | ||
| 241 | T::common_regs().ccr().modify(|reg| { | ||
| 242 | reg.set_vsenseen(true); | ||
| 243 | }); | 241 | }); |
| 244 | |||
| 245 | super::Temperature {} | ||
| 246 | } | 242 | } |
| 247 | 243 | ||
| 248 | /// Enable reading the vbat internal channel. | 244 | /// Stop regular conversions and disable DMA |
| 249 | pub fn enable_vbat(&self) -> super::Vbat | 245 | pub(super) fn stop() { |
| 250 | where | 246 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 251 | T: super::VBatConverter, | 247 | T::regs().cr().modify(|reg| { |
| 252 | { | 248 | reg.set_adstp(Adstp::STOP); |
| 253 | T::common_regs().ccr().modify(|reg| { | 249 | }); |
| 254 | reg.set_vbaten(true); | 250 | // The software must poll ADSTART until the bit is reset before assuming the |
| 255 | }); | 251 | // ADC is completely stopped |
| 256 | 252 | while T::regs().cr().read().adstart() {} | |
| 257 | super::Vbat {} | 253 | } |
| 258 | } | ||
| 259 | 254 | ||
| 260 | /// Enable differential channel. | 255 | // Disable dma control and continuous conversion, if enabled |
| 261 | /// Caution: | 256 | T::regs().cfgr().modify(|reg| { |
| 262 | /// : When configuring the channel “i” in differential input mode, its negative input voltage VINN[i] | 257 | reg.set_cont(false); |
| 263 | /// is connected to another channel. As a consequence, this channel is no longer usable in | 258 | reg.set_dmaen(Dmaen::DISABLE); |
| 264 | /// single-ended mode or in differential mode and must never be configured to be converted. | ||
| 265 | /// Some channels are shared between ADC1/ADC2/ADC3/ADC4/ADC5: this can make the | ||
| 266 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | ||
| 267 | /// operate in interleaved mode. | ||
| 268 | #[cfg(stm32g4)] | ||
| 269 | fn set_differential_channel(&mut self, ch: usize, enable: bool) { | ||
| 270 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 271 | T::regs().difsel().modify(|w| { | ||
| 272 | w.set_difsel( | ||
| 273 | ch, | ||
| 274 | if enable { | ||
| 275 | Difsel::DIFFERENTIAL | ||
| 276 | } else { | ||
| 277 | Difsel::SINGLE_ENDED | ||
| 278 | }, | ||
| 279 | ); | ||
| 280 | }); | 259 | }); |
| 281 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 282 | } | ||
| 283 | |||
| 284 | #[cfg(stm32g4)] | ||
| 285 | pub fn set_differential(&mut self, channel: &mut impl AdcChannel<T>, enable: bool) { | ||
| 286 | self.set_differential_channel(channel.channel() as usize, enable); | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Set oversampling shift. | ||
| 290 | #[cfg(stm32g4)] | ||
| 291 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 292 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Set oversampling ratio. | ||
| 296 | #[cfg(stm32g4)] | ||
| 297 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 298 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Enable oversampling in regular mode. | ||
| 302 | #[cfg(stm32g4)] | ||
| 303 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 304 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 305 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 306 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 307 | } | ||
| 308 | |||
| 309 | // Reads that are not implemented as INJECTED in "blocking_read" | ||
| 310 | // #[cfg(stm32g4)] | ||
| 311 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 312 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 313 | // } | ||
| 314 | |||
| 315 | // #[cfg(stm32g4)] | ||
| 316 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { | ||
| 317 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 318 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 319 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 320 | // } | ||
| 321 | |||
| 322 | /// Set the ADC resolution. | ||
| 323 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 324 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 325 | } | 260 | } |
| 326 | 261 | ||
| 327 | /// Perform a single conversion. | 262 | /// Perform a single conversion. |
| 328 | fn convert(&mut self) -> u16 { | 263 | pub(super) fn convert() -> u16 { |
| 329 | T::regs().isr().modify(|reg| { | 264 | T::regs().isr().modify(|reg| { |
| 330 | reg.set_eos(true); | 265 | reg.set_eos(true); |
| 331 | reg.set_eoc(true); | 266 | reg.set_eoc(true); |
| @@ -343,138 +278,52 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 343 | T::regs().dr().read().0 as u16 | 278 | T::regs().dr().read().0 as u16 |
| 344 | } | 279 | } |
| 345 | 280 | ||
| 346 | /// Read an ADC pin. | 281 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 347 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 348 | channel.setup(); | ||
| 349 | |||
| 350 | self.read_channel(channel, sample_time) | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Start regular adc conversion | ||
| 354 | pub(super) fn start() { | ||
| 355 | T::regs().cr().modify(|reg| { | ||
| 356 | reg.set_adstart(true); | ||
| 357 | }); | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Stop regular conversions | ||
| 361 | pub(super) fn stop() { | ||
| 362 | Self::stop_regular_conversions(); | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Teardown method for stopping regular ADC conversions | ||
| 366 | pub(super) fn teardown_dma() { | ||
| 367 | Self::stop_regular_conversions(); | ||
| 368 | |||
| 369 | // Disable dma control | ||
| 370 | T::regs().cfgr().modify(|reg| { | ||
| 371 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | |||
| 375 | /// Read one or multiple ADC regular channels using DMA. | ||
| 376 | /// | ||
| 377 | /// `sequence` iterator and `readings` must have the same length. | ||
| 378 | /// | ||
| 379 | /// Example | ||
| 380 | /// ```rust,ignore | ||
| 381 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 382 | /// | ||
| 383 | /// let mut adc = Adc::new(p.ADC1); | ||
| 384 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 385 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 386 | /// let mut measurements = [0u16; 2]; | ||
| 387 | /// | ||
| 388 | /// adc.read( | ||
| 389 | /// p.DMA1_CH2.reborrow(), | ||
| 390 | /// [ | ||
| 391 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 392 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 393 | /// ] | ||
| 394 | /// .into_iter(), | ||
| 395 | /// &mut measurements, | ||
| 396 | /// ) | ||
| 397 | /// .await; | ||
| 398 | /// defmt::info!("measurements: {}", measurements); | ||
| 399 | /// ``` | ||
| 400 | /// | ||
| 401 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 402 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 403 | pub async fn read( | ||
| 404 | &mut self, | ||
| 405 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 406 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 407 | readings: &mut [u16], | ||
| 408 | ) { | ||
| 409 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 410 | assert!( | ||
| 411 | sequence.len() == readings.len(), | ||
| 412 | "Sequence length must be equal to readings length" | ||
| 413 | ); | ||
| 414 | assert!( | ||
| 415 | sequence.len() <= 16, | ||
| 416 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 417 | ); | ||
| 418 | |||
| 419 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 420 | Self::stop_regular_conversions(); | ||
| 421 | Self::enable(); | ||
| 422 | |||
| 423 | Self::configure_sequence(sequence.map(|(channel, sample_time)| { | ||
| 424 | channel.setup(); | ||
| 425 | |||
| 426 | (channel.channel, sample_time) | ||
| 427 | })); | ||
| 428 | |||
| 429 | // Set continuous mode with oneshot dma. | ||
| 430 | // Clear overrun flag before starting transfer. | ||
| 431 | T::regs().isr().modify(|reg| { | 282 | T::regs().isr().modify(|reg| { |
| 432 | reg.set_ovr(true); | 283 | reg.set_ovr(true); |
| 433 | }); | 284 | }); |
| 434 | 285 | ||
| 435 | T::regs().cfgr().modify(|reg| { | 286 | T::regs().cfgr().modify(|reg| { |
| 436 | reg.set_discen(false); | 287 | reg.set_discen(false); // Convert all channels for each trigger |
| 437 | reg.set_cont(true); | 288 | reg.set_dmacfg(match conversion_mode { |
| 438 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 289 | ConversionMode::Singular => Dmacfg::ONE_SHOT, |
| 290 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 291 | }); | ||
| 439 | reg.set_dmaen(Dmaen::ENABLE); | 292 | reg.set_dmaen(Dmaen::ENABLE); |
| 440 | }); | 293 | }); |
| 441 | 294 | ||
| 442 | let request = rx_dma.request(); | 295 | if let ConversionMode::Repeated(mode) = conversion_mode { |
| 443 | let transfer = unsafe { | 296 | match mode { |
| 444 | Transfer::new_read( | 297 | RegularConversionMode::Continuous => { |
| 445 | rx_dma, | 298 | T::regs().cfgr().modify(|reg| { |
| 446 | request, | 299 | reg.set_cont(true); |
| 447 | T::regs().dr().as_ptr() as *mut u16, | 300 | }); |
| 448 | readings, | 301 | } |
| 449 | Default::default(), | 302 | RegularConversionMode::Triggered(trigger) => { |
| 450 | ) | 303 | T::regs().cfgr().modify(|r| { |
| 451 | }; | 304 | r.set_cont(false); // New trigger is neede for each sample to be read |
| 452 | 305 | }); | |
| 453 | // Start conversion | ||
| 454 | T::regs().cr().modify(|reg| { | ||
| 455 | reg.set_adstart(true); | ||
| 456 | }); | ||
| 457 | |||
| 458 | // Wait for conversion sequence to finish. | ||
| 459 | transfer.await; | ||
| 460 | 306 | ||
| 461 | // Ensure conversions are finished. | 307 | T::regs().cfgr().modify(|r| { |
| 462 | Self::stop_regular_conversions(); | 308 | r.set_extsel(trigger.channel); |
| 309 | r.set_exten(trigger.edge); | ||
| 310 | }); | ||
| 463 | 311 | ||
| 464 | // Reset configuration. | 312 | // Regular conversions uses DMA so no need to generate interrupt |
| 465 | T::regs().cfgr().modify(|reg| { | 313 | T::regs().ier().modify(|r| r.set_eosie(false)); |
| 466 | reg.set_cont(false); | 314 | } |
| 467 | }); | 315 | } |
| 316 | } | ||
| 468 | } | 317 | } |
| 469 | 318 | ||
| 470 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | 319 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 471 | // Set sequence length | 320 | // Set sequence length |
| 472 | T::regs().sqr1().modify(|w| { | 321 | T::regs().sqr1().modify(|w| { |
| 473 | w.set_l(sequence.len() as u8 - 1); | 322 | w.set_l(sequence.len() as u8 - 1); |
| 474 | }); | 323 | }); |
| 475 | 324 | ||
| 476 | // Configure channels and ranks | 325 | // Configure channels and ranks |
| 477 | for (_i, (ch, sample_time)) in sequence.enumerate() { | 326 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { |
| 478 | let sample_time = sample_time.into(); | 327 | let sample_time = sample_time.into(); |
| 479 | if ch <= 9 { | 328 | if ch <= 9 { |
| 480 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | 329 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); |
| @@ -505,101 +354,75 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 505 | } | 354 | } |
| 506 | _ => unreachable!(), | 355 | _ => unreachable!(), |
| 507 | } | 356 | } |
| 357 | |||
| 358 | #[cfg(stm32g4)] | ||
| 359 | { | ||
| 360 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 361 | |||
| 362 | T::regs().difsel().modify(|w| { | ||
| 363 | w.set_difsel( | ||
| 364 | ch.into(), | ||
| 365 | if is_differential { | ||
| 366 | Difsel::DIFFERENTIAL | ||
| 367 | } else { | ||
| 368 | Difsel::SINGLE_ENDED | ||
| 369 | }, | ||
| 370 | ); | ||
| 371 | }); | ||
| 372 | |||
| 373 | T::regs().cr().modify(|w| w.set_aden(true)); // enable adc | ||
| 374 | } | ||
| 508 | } | 375 | } |
| 509 | } | 376 | } |
| 510 | 377 | ||
| 511 | /// Set external trigger for regular conversion sequence | 378 | /// Enable reading the voltage reference internal channel. |
| 512 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | 379 | pub fn enable_vrefint(&self) -> super::VrefInt |
| 513 | T::regs().cfgr().modify(|r| { | 380 | where |
| 514 | r.set_extsel(trigger.channel); | 381 | T: super::SpecialConverter<super::VrefInt>, |
| 515 | r.set_exten(trigger.edge); | 382 | { |
| 383 | T::common_regs().ccr().modify(|reg| { | ||
| 384 | reg.set_vrefen(true); | ||
| 516 | }); | 385 | }); |
| 517 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 518 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 519 | } | ||
| 520 | 386 | ||
| 521 | // Dual ADC mode selection | 387 | super::VrefInt {} |
| 522 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 523 | T::common_regs().ccr().modify(|reg| { | ||
| 524 | reg.set_dual(val); | ||
| 525 | }) | ||
| 526 | } | 388 | } |
| 527 | 389 | ||
| 528 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 390 | /// Enable reading the temperature internal channel. |
| 529 | /// | 391 | pub fn enable_temperature(&self) -> super::Temperature |
| 530 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | 392 | where |
| 531 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | 393 | T: super::SpecialConverter<super::Temperature>, |
| 532 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | 394 | { |
| 533 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | 395 | T::common_regs().ccr().modify(|reg| { |
| 534 | /// defines the period at which the buffer should be read. | 396 | reg.set_vsenseen(true); |
| 535 | /// | ||
| 536 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 537 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 538 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 539 | /// the buffer length should be `3 * 40 = 120`. | ||
| 540 | /// | ||
| 541 | /// # Parameters | ||
| 542 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 543 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 544 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 545 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 546 | /// | ||
| 547 | /// # Returns | ||
| 548 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 549 | pub fn into_ring_buffered<'a>( | ||
| 550 | mut self, | ||
| 551 | dma: Peri<'a, impl RxDma<T>>, | ||
| 552 | dma_buf: &'a mut [u16], | ||
| 553 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 554 | mode: RegularConversionMode, | ||
| 555 | ) -> RingBufferedAdc<'a, T> { | ||
| 556 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 557 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 558 | assert!( | ||
| 559 | sequence.len() <= 16, | ||
| 560 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 561 | ); | ||
| 562 | // reset conversions and enable the adc | ||
| 563 | Self::stop_regular_conversions(); | ||
| 564 | Self::enable(); | ||
| 565 | |||
| 566 | //adc side setup | ||
| 567 | |||
| 568 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 569 | channel.setup(); | ||
| 570 | |||
| 571 | (channel.channel, sample_time) | ||
| 572 | })); | ||
| 573 | |||
| 574 | // Clear overrun flag before starting transfer. | ||
| 575 | T::regs().isr().modify(|reg| { | ||
| 576 | reg.set_ovr(true); | ||
| 577 | }); | 397 | }); |
| 578 | 398 | ||
| 579 | T::regs().cfgr().modify(|reg| { | 399 | super::Temperature {} |
| 580 | reg.set_discen(false); // Convert all channels for each trigger | 400 | } |
| 581 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 401 | |
| 582 | reg.set_dmaen(Dmaen::ENABLE); | 402 | /// Enable reading the vbat internal channel. |
| 403 | pub fn enable_vbat(&self) -> super::Vbat | ||
| 404 | where | ||
| 405 | T: super::SpecialConverter<super::Vbat>, | ||
| 406 | { | ||
| 407 | T::common_regs().ccr().modify(|reg| { | ||
| 408 | reg.set_vbaten(true); | ||
| 583 | }); | 409 | }); |
| 584 | 410 | ||
| 585 | match mode { | 411 | super::Vbat {} |
| 586 | RegularConversionMode::Continuous => { | 412 | } |
| 587 | T::regs().cfgr().modify(|reg| { | ||
| 588 | reg.set_cont(true); | ||
| 589 | }); | ||
| 590 | } | ||
| 591 | RegularConversionMode::Triggered(trigger) => { | ||
| 592 | T::regs().cfgr().modify(|r| { | ||
| 593 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 594 | }); | ||
| 595 | self.set_regular_conversion_trigger(trigger); | ||
| 596 | } | ||
| 597 | } | ||
| 598 | 413 | ||
| 599 | mem::forget(self); | 414 | // Reads that are not implemented as INJECTED in "blocking_read" |
| 415 | // #[cfg(stm32g4)] | ||
| 416 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 417 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 418 | // } | ||
| 600 | 419 | ||
| 601 | RingBufferedAdc::new(dma, dma_buf) | 420 | // #[cfg(stm32g4)] |
| 602 | } | 421 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { |
| 422 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 423 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 424 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 425 | // } | ||
| 603 | 426 | ||
| 604 | /// Configures the ADC for injected conversions. | 427 | /// Configures the ADC for injected conversions. |
| 605 | /// | 428 | /// |
| @@ -629,7 +452,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 629 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | 452 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 630 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | 453 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 631 | pub fn setup_injected_conversions<'a, const N: usize>( | 454 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 632 | mut self, | 455 | self, |
| 633 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | 456 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 634 | trigger: ConversionTrigger, | 457 | trigger: ConversionTrigger, |
| 635 | interrupt: bool, | 458 | interrupt: bool, |
| @@ -641,13 +464,22 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 641 | NR_INJECTED_RANKS | 464 | NR_INJECTED_RANKS |
| 642 | ); | 465 | ); |
| 643 | 466 | ||
| 644 | Self::stop_regular_conversions(); | 467 | Self::stop(); |
| 645 | Self::enable(); | 468 | Self::enable(); |
| 646 | 469 | ||
| 647 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | 470 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 648 | 471 | ||
| 649 | for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() { | 472 | for (n, (channel, sample_time)) in sequence.into_iter().enumerate() { |
| 650 | Self::configure_channel(&mut channel, sample_time); | 473 | let sample_time = sample_time.into(); |
| 474 | if channel.channel() <= 9 { | ||
| 475 | T::regs() | ||
| 476 | .smpr() | ||
| 477 | .modify(|reg| reg.set_smp(channel.channel() as _, sample_time)); | ||
| 478 | } else { | ||
| 479 | T::regs() | ||
| 480 | .smpr2() | ||
| 481 | .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time)); | ||
| 482 | } | ||
| 651 | 483 | ||
| 652 | let idx = match n { | 484 | let idx = match n { |
| 653 | 0..=3 => n, | 485 | 0..=3 => n, |
| @@ -662,8 +494,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 662 | 494 | ||
| 663 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | 495 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 664 | 496 | ||
| 665 | self.set_injected_conversion_trigger(trigger); | 497 | // Set external trigger for injected conversion sequence |
| 666 | self.enable_injected_eos_interrupt(interrupt); | 498 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 499 | T::regs().jsqr().modify(|r| { | ||
| 500 | r.set_jextsel(trigger.channel); | ||
| 501 | r.set_jexten(trigger.edge); | ||
| 502 | }); | ||
| 503 | |||
| 504 | // Enable end of injected sequence interrupt | ||
| 505 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); | ||
| 506 | |||
| 667 | Self::start_injected_conversions(); | 507 | Self::start_injected_conversions(); |
| 668 | 508 | ||
| 669 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | 509 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| @@ -701,7 +541,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 701 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | 541 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 702 | injected_trigger: ConversionTrigger, | 542 | injected_trigger: ConversionTrigger, |
| 703 | injected_interrupt: bool, | 543 | injected_interrupt: bool, |
| 704 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | 544 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { |
| 705 | unsafe { | 545 | unsafe { |
| 706 | ( | 546 | ( |
| 707 | Self { | 547 | Self { |
| @@ -734,64 +574,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 734 | reg.set_jadstart(true); | 574 | reg.set_jadstart(true); |
| 735 | }); | 575 | }); |
| 736 | } | 576 | } |
| 737 | |||
| 738 | /// Set external trigger for injected conversion sequence | ||
| 739 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 740 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 741 | T::regs().jsqr().modify(|r| { | ||
| 742 | r.set_jextsel(trigger.channel); | ||
| 743 | r.set_jexten(trigger.edge); | ||
| 744 | }); | ||
| 745 | } | ||
| 746 | |||
| 747 | /// Enable end of injected sequence interrupt | ||
| 748 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 749 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 750 | } | ||
| 751 | |||
| 752 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 753 | // Configure channel | ||
| 754 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 755 | } | ||
| 756 | |||
| 757 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 758 | Self::configure_channel(channel, sample_time); | ||
| 759 | #[cfg(stm32h7)] | ||
| 760 | { | ||
| 761 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 762 | T::regs() | ||
| 763 | .pcsel() | ||
| 764 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 765 | } | ||
| 766 | |||
| 767 | T::regs().sqr1().write(|reg| { | ||
| 768 | reg.set_sq(0, channel.channel()); | ||
| 769 | reg.set_l(0); | ||
| 770 | }); | ||
| 771 | |||
| 772 | self.convert() | ||
| 773 | } | ||
| 774 | |||
| 775 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 776 | let sample_time = sample_time.into(); | ||
| 777 | if ch <= 9 { | ||
| 778 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 779 | } else { | ||
| 780 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | // Stop regular conversions | ||
| 785 | fn stop_regular_conversions() { | ||
| 786 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 787 | T::regs().cr().modify(|reg| { | ||
| 788 | reg.set_adstp(Adstp::STOP); | ||
| 789 | }); | ||
| 790 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 791 | // ADC is completely stopped | ||
| 792 | while T::regs().cr().read().adstart() {} | ||
| 793 | } | ||
| 794 | } | ||
| 795 | } | 577 | } |
| 796 | 578 | ||
| 797 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 579 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| @@ -811,47 +593,47 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 811 | 593 | ||
| 812 | #[cfg(stm32g4)] | 594 | #[cfg(stm32g4)] |
| 813 | mod g4 { | 595 | mod g4 { |
| 814 | use crate::adc::{TemperatureConverter, VBatConverter, VrefConverter}; | 596 | use crate::adc::{SealedSpecialConverter, Temperature, Vbat, VrefInt}; |
| 815 | 597 | ||
| 816 | impl TemperatureConverter for crate::peripherals::ADC1 { | 598 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC1 { |
| 817 | const CHANNEL: u8 = 16; | 599 | const CHANNEL: u8 = 16; |
| 818 | } | 600 | } |
| 819 | 601 | ||
| 820 | impl VrefConverter for crate::peripherals::ADC1 { | 602 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC1 { |
| 821 | const CHANNEL: u8 = 18; | 603 | const CHANNEL: u8 = 18; |
| 822 | } | 604 | } |
| 823 | 605 | ||
| 824 | impl VBatConverter for crate::peripherals::ADC1 { | 606 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC1 { |
| 825 | const CHANNEL: u8 = 17; | 607 | const CHANNEL: u8 = 17; |
| 826 | } | 608 | } |
| 827 | 609 | ||
| 828 | #[cfg(peri_adc3_common)] | 610 | #[cfg(peri_adc3_common)] |
| 829 | impl VrefConverter for crate::peripherals::ADC3 { | 611 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC3 { |
| 830 | const CHANNEL: u8 = 18; | 612 | const CHANNEL: u8 = 18; |
| 831 | } | 613 | } |
| 832 | 614 | ||
| 833 | #[cfg(peri_adc3_common)] | 615 | #[cfg(peri_adc3_common)] |
| 834 | impl VBatConverter for crate::peripherals::ADC3 { | 616 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC3 { |
| 835 | const CHANNEL: u8 = 17; | 617 | const CHANNEL: u8 = 17; |
| 836 | } | 618 | } |
| 837 | 619 | ||
| 838 | #[cfg(not(stm32g4x1))] | 620 | #[cfg(not(stm32g4x1))] |
| 839 | impl VrefConverter for crate::peripherals::ADC4 { | 621 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC4 { |
| 840 | const CHANNEL: u8 = 18; | 622 | const CHANNEL: u8 = 18; |
| 841 | } | 623 | } |
| 842 | 624 | ||
| 843 | #[cfg(not(stm32g4x1))] | 625 | #[cfg(not(stm32g4x1))] |
| 844 | impl TemperatureConverter for crate::peripherals::ADC5 { | 626 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC5 { |
| 845 | const CHANNEL: u8 = 4; | 627 | const CHANNEL: u8 = 4; |
| 846 | } | 628 | } |
| 847 | 629 | ||
| 848 | #[cfg(not(stm32g4x1))] | 630 | #[cfg(not(stm32g4x1))] |
| 849 | impl VrefConverter for crate::peripherals::ADC5 { | 631 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC5 { |
| 850 | const CHANNEL: u8 = 18; | 632 | const CHANNEL: u8 = 18; |
| 851 | } | 633 | } |
| 852 | 634 | ||
| 853 | #[cfg(not(stm32g4x1))] | 635 | #[cfg(not(stm32g4x1))] |
| 854 | impl VBatConverter for crate::peripherals::ADC5 { | 636 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC5 { |
| 855 | const CHANNEL: u8 = 17; | 637 | const CHANNEL: u8 = 17; |
| 856 | } | 638 | } |
| 857 | } | 639 | } |
| @@ -859,13 +641,13 @@ mod g4 { | |||
| 859 | // TODO this should look at each ADC individually and impl the correct channels | 641 | // TODO this should look at each ADC individually and impl the correct channels |
| 860 | #[cfg(stm32h7)] | 642 | #[cfg(stm32h7)] |
| 861 | mod h7 { | 643 | mod h7 { |
| 862 | impl<T: Instance> TemperatureConverter for T { | 644 | impl<T: Instance> SealedSpecialConverter<Temperature> for T { |
| 863 | const CHANNEL: u8 = 18; | 645 | const CHANNEL: u8 = 18; |
| 864 | } | 646 | } |
| 865 | impl<T: Instance> VrefConverter for T { | 647 | impl<T: Instance> SealedSpecialConverter<VrefInt> for T { |
| 866 | const CHANNEL: u8 = 19; | 648 | const CHANNEL: u8 = 19; |
| 867 | } | 649 | } |
| 868 | impl<T: Instance> VBatConverter for T { | 650 | impl<T: Instance> SealedSpecialConverter<Vbat> for T { |
| 869 | // TODO this should be 14 for H7a/b/35 | 651 | // TODO this should be 14 for H7a/b/35 |
| 870 | const CHANNEL: u8 = 17; | 652 | const CHANNEL: u8 = 17; |
| 871 | } | 653 | } |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..7bb3a541c 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -38,7 +38,7 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 38 | 38 | ||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { |
| 40 | fn drop(&mut self) { | 40 | fn drop(&mut self) { |
| 41 | Adc::<T>::teardown_dma(); | 41 | Adc::<T>::stop(); |
| 42 | compiler_fence(Ordering::SeqCst); | 42 | compiler_fence(Ordering::SeqCst); |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 3bf893a35..bf404d6ef 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,6 +17,9 @@ | |||
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | 17 | #[cfg_attr(adc_c0, path = "c0.rs")] |
| 18 | mod _version; | 18 | mod _version; |
| 19 | 19 | ||
| 20 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 21 | mod ringbuffered; | ||
| 22 | |||
| 20 | use core::marker::PhantomData; | 23 | use core::marker::PhantomData; |
| 21 | 24 | ||
| 22 | #[allow(unused)] | 25 | #[allow(unused)] |
| @@ -25,6 +28,8 @@ pub use _version::*; | |||
| 25 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 29 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 30 | use embassy_sync::waitqueue::AtomicWaker; |
| 31 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 32 | pub use ringbuffered::RingBufferedAdc; | ||
| 28 | 33 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 34 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "adc4.rs"] | 35 | #[path = "adc4.rs"] |
| @@ -80,6 +85,11 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 80 | 85 | ||
| 81 | #[allow(unused)] | 86 | #[allow(unused)] |
| 82 | fn channel(&self) -> u8; | 87 | fn channel(&self) -> u8; |
| 88 | |||
| 89 | #[allow(unused)] | ||
| 90 | fn is_differential(&self) -> bool { | ||
| 91 | false | ||
| 92 | } | ||
| 83 | } | 93 | } |
| 84 | 94 | ||
| 85 | /// Performs a busy-wait delay for a specified number of microseconds. | 95 | /// Performs a busy-wait delay for a specified number of microseconds. |
| @@ -100,29 +110,188 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 100 | } | 110 | } |
| 101 | } | 111 | } |
| 102 | 112 | ||
| 103 | /// Implemented for ADCs that have a Temperature channel | 113 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] |
| 104 | pub trait TemperatureConverter { | 114 | pub(self) enum ConversionMode { |
| 105 | const CHANNEL: u8; | 115 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] |
| 116 | Singular, | ||
| 117 | #[allow(dead_code)] | ||
| 118 | Repeated(RegularConversionMode), | ||
| 106 | } | 119 | } |
| 107 | /// Implemented for ADCs that have a Vref channel | 120 | |
| 108 | pub trait VrefConverter { | 121 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] |
| 109 | const CHANNEL: u8; | 122 | // Conversion mode for regular ADC channels |
| 123 | #[derive(Copy, Clone)] | ||
| 124 | pub enum RegularConversionMode { | ||
| 125 | // Samples as fast as possible | ||
| 126 | Continuous, | ||
| 127 | #[cfg(adc_g4)] | ||
| 128 | // Sample at rate determined by external trigger | ||
| 129 | Triggered(ConversionTrigger), | ||
| 110 | } | 130 | } |
| 111 | /// Implemented for ADCs that have a VBat channel | 131 | |
| 112 | pub trait VBatConverter { | 132 | impl<'d, T: Instance> Adc<'d, T> { |
| 133 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4))] | ||
| 134 | /// Read an ADC pin. | ||
| 135 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 136 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 137 | channel.setup(); | ||
| 138 | |||
| 139 | #[cfg(not(adc_v4))] | ||
| 140 | Self::enable(); | ||
| 141 | Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 142 | |||
| 143 | Self::convert() | ||
| 144 | } | ||
| 145 | |||
| 146 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 147 | /// Read one or multiple ADC regular channels using DMA. | ||
| 148 | /// | ||
| 149 | /// `sequence` iterator and `readings` must have the same length. | ||
| 150 | /// | ||
| 151 | /// Example | ||
| 152 | /// ```rust,ignore | ||
| 153 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 154 | /// | ||
| 155 | /// let mut adc = Adc::new(p.ADC1); | ||
| 156 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 157 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 158 | /// let mut measurements = [0u16; 2]; | ||
| 159 | /// | ||
| 160 | /// adc.read( | ||
| 161 | /// p.DMA1_CH2.reborrow(), | ||
| 162 | /// [ | ||
| 163 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 164 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 165 | /// ] | ||
| 166 | /// .into_iter(), | ||
| 167 | /// &mut measurements, | ||
| 168 | /// ) | ||
| 169 | /// .await; | ||
| 170 | /// defmt::info!("measurements: {}", measurements); | ||
| 171 | /// ``` | ||
| 172 | /// | ||
| 173 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 174 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 175 | pub async fn read( | ||
| 176 | &mut self, | ||
| 177 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 178 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 179 | readings: &mut [u16], | ||
| 180 | ) { | ||
| 181 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 182 | assert!( | ||
| 183 | sequence.len() == readings.len(), | ||
| 184 | "Sequence length must be equal to readings length" | ||
| 185 | ); | ||
| 186 | assert!( | ||
| 187 | sequence.len() <= 16, | ||
| 188 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 189 | ); | ||
| 190 | |||
| 191 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 192 | Self::stop(); | ||
| 193 | Self::enable(); | ||
| 194 | |||
| 195 | Self::configure_sequence( | ||
| 196 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 197 | ); | ||
| 198 | |||
| 199 | Self::configure_dma(ConversionMode::Singular); | ||
| 200 | |||
| 201 | let request = rx_dma.request(); | ||
| 202 | let transfer = unsafe { | ||
| 203 | crate::dma::Transfer::new_read( | ||
| 204 | rx_dma, | ||
| 205 | request, | ||
| 206 | T::regs().dr().as_ptr() as *mut u16, | ||
| 207 | readings, | ||
| 208 | Default::default(), | ||
| 209 | ) | ||
| 210 | }; | ||
| 211 | |||
| 212 | Self::start(); | ||
| 213 | |||
| 214 | // Wait for conversion sequence to finish. | ||
| 215 | transfer.await; | ||
| 216 | |||
| 217 | // Ensure conversions are finished. | ||
| 218 | Self::stop(); | ||
| 219 | } | ||
| 220 | |||
| 221 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 222 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 223 | /// | ||
| 224 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 225 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 226 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 227 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 228 | /// defines the period at which the buffer should be read. | ||
| 229 | /// | ||
| 230 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 231 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 232 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 233 | /// the buffer length should be `3 * 40 = 120`. | ||
| 234 | /// | ||
| 235 | /// # Parameters | ||
| 236 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 237 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 238 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 239 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 240 | /// | ||
| 241 | /// # Returns | ||
| 242 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 243 | pub fn into_ring_buffered<'a>( | ||
| 244 | self, | ||
| 245 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 246 | dma_buf: &'a mut [u16], | ||
| 247 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 248 | mode: RegularConversionMode, | ||
| 249 | ) -> RingBufferedAdc<'a, T> { | ||
| 250 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 251 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 252 | assert!( | ||
| 253 | sequence.len() <= 16, | ||
| 254 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 255 | ); | ||
| 256 | // reset conversions and enable the adc | ||
| 257 | Self::stop(); | ||
| 258 | Self::enable(); | ||
| 259 | |||
| 260 | //adc side setup | ||
| 261 | Self::configure_sequence( | ||
| 262 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 263 | ); | ||
| 264 | |||
| 265 | Self::configure_dma(ConversionMode::Repeated(mode)); | ||
| 266 | |||
| 267 | core::mem::forget(self); | ||
| 268 | |||
| 269 | RingBufferedAdc::new(dma, dma_buf) | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | pub(self) trait SpecialChannel {} | ||
| 274 | |||
| 275 | /// Implemented for ADCs that have a special channel | ||
| 276 | trait SealedSpecialConverter<T: SpecialChannel + Sized> { | ||
| 113 | const CHANNEL: u8; | 277 | const CHANNEL: u8; |
| 114 | } | 278 | } |
| 115 | 279 | ||
| 116 | // 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 | 280 | #[allow(private_bounds)] |
| 117 | /// Internal voltage reference channel. | 281 | pub trait SpecialConverter<T: SpecialChannel + Sized>: SealedSpecialConverter<T> {} |
| 118 | pub struct VrefInt; | 282 | |
| 119 | impl<T: Instance + VrefConverter> AdcChannel<T> for VrefInt {} | 283 | impl<C: SpecialChannel + Sized, T: SealedSpecialConverter<C>> SpecialConverter<C> for T {} |
| 120 | impl<T: Instance + VrefConverter> SealedAdcChannel<T> for VrefInt { | 284 | |
| 285 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> AdcChannel<T> for C {} | ||
| 286 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> SealedAdcChannel<T> for C { | ||
| 121 | fn channel(&self) -> u8 { | 287 | fn channel(&self) -> u8 { |
| 122 | T::CHANNEL | 288 | T::CHANNEL |
| 123 | } | 289 | } |
| 124 | } | 290 | } |
| 125 | 291 | ||
| 292 | pub struct VrefInt; | ||
| 293 | impl SpecialChannel for VrefInt {} | ||
| 294 | |||
| 126 | impl VrefInt { | 295 | impl VrefInt { |
| 127 | #[cfg(any(adc_f3v1, adc_f3v2))] | 296 | #[cfg(any(adc_f3v1, adc_f3v2))] |
| 128 | /// The value that vref would be if vdda was at 3300mv | 297 | /// The value that vref would be if vdda was at 3300mv |
| @@ -133,21 +302,19 @@ impl VrefInt { | |||
| 133 | 302 | ||
| 134 | /// Internal temperature channel. | 303 | /// Internal temperature channel. |
| 135 | pub struct Temperature; | 304 | pub struct Temperature; |
| 136 | impl<T: Instance + TemperatureConverter> AdcChannel<T> for Temperature {} | 305 | impl SpecialChannel for Temperature {} |
| 137 | impl<T: Instance + TemperatureConverter> SealedAdcChannel<T> for Temperature { | ||
| 138 | fn channel(&self) -> u8 { | ||
| 139 | T::CHANNEL | ||
| 140 | } | ||
| 141 | } | ||
| 142 | 306 | ||
| 143 | /// Internal battery voltage channel. | 307 | /// Internal battery voltage channel. |
| 144 | pub struct Vbat; | 308 | pub struct Vbat; |
| 145 | impl<T: Instance + VBatConverter> AdcChannel<T> for Vbat {} | 309 | impl SpecialChannel for Vbat {} |
| 146 | impl<T: Instance + VBatConverter> SealedAdcChannel<T> for Vbat { | 310 | |
| 147 | fn channel(&self) -> u8 { | 311 | /// Vcore channel. |
| 148 | T::CHANNEL | 312 | pub struct Vcore; |
| 149 | } | 313 | impl SpecialChannel for Vcore {} |
| 150 | } | 314 | |
| 315 | /// Internal dac channel. | ||
| 316 | pub struct Dac; | ||
| 317 | impl SpecialChannel for Dac {} | ||
| 151 | 318 | ||
| 152 | /// ADC instance. | 319 | /// ADC instance. |
| 153 | #[cfg(not(any( | 320 | #[cfg(not(any( |
| @@ -178,6 +345,7 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 178 | 345 | ||
| 179 | AnyAdcChannel { | 346 | AnyAdcChannel { |
| 180 | channel: self.channel(), | 347 | channel: self.channel(), |
| 348 | is_differential: self.is_differential(), | ||
| 181 | _phantom: PhantomData, | 349 | _phantom: PhantomData, |
| 182 | } | 350 | } |
| 183 | } | 351 | } |
| @@ -189,6 +357,7 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 189 | /// storing them in an array. | 357 | /// storing them in an array. |
| 190 | pub struct AnyAdcChannel<T> { | 358 | pub struct AnyAdcChannel<T> { |
| 191 | channel: u8, | 359 | channel: u8, |
| 360 | is_differential: bool, | ||
| 192 | _phantom: PhantomData<T>, | 361 | _phantom: PhantomData<T>, |
| 193 | } | 362 | } |
| 194 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 363 | impl_peripheral!(AnyAdcChannel<T: Instance>); |
| @@ -197,6 +366,10 @@ impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | |||
| 197 | fn channel(&self) -> u8 { | 366 | fn channel(&self) -> u8 { |
| 198 | self.channel | 367 | self.channel |
| 199 | } | 368 | } |
| 369 | |||
| 370 | fn is_differential(&self) -> bool { | ||
| 371 | self.is_differential | ||
| 372 | } | ||
| 200 | } | 373 | } |
| 201 | 374 | ||
| 202 | impl<T> AnyAdcChannel<T> { | 375 | impl<T> AnyAdcChannel<T> { |
| @@ -315,6 +488,39 @@ macro_rules! impl_adc_pin { | |||
| 315 | }; | 488 | }; |
| 316 | } | 489 | } |
| 317 | 490 | ||
| 491 | #[allow(unused_macros)] | ||
| 492 | macro_rules! impl_adc_pair { | ||
| 493 | ($inst:ident, $pin:ident, $npin:ident, $ch:expr) => { | ||
| 494 | impl crate::adc::AdcChannel<peripherals::$inst> | ||
| 495 | for ( | ||
| 496 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 497 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 498 | ) | ||
| 499 | { | ||
| 500 | } | ||
| 501 | impl crate::adc::SealedAdcChannel<peripherals::$inst> | ||
| 502 | for ( | ||
| 503 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 504 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 505 | ) | ||
| 506 | { | ||
| 507 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 508 | fn setup(&mut self) { | ||
| 509 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0); | ||
| 510 | <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1); | ||
| 511 | } | ||
| 512 | |||
| 513 | fn channel(&self) -> u8 { | ||
| 514 | $ch | ||
| 515 | } | ||
| 516 | |||
| 517 | fn is_differential(&self) -> bool { | ||
| 518 | true | ||
| 519 | } | ||
| 520 | } | ||
| 521 | }; | ||
| 522 | } | ||
| 523 | |||
| 318 | /// Get the maximum reading value for this resolution. | 524 | /// Get the maximum reading value for this resolution. |
| 319 | /// | 525 | /// |
| 320 | /// This is `2**n - 1`. | 526 | /// This is `2**n - 1`. |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..62ea0d3a2 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -172,7 +172,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 172 | 172 | ||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { |
| 174 | fn drop(&mut self) { | 174 | fn drop(&mut self) { |
| 175 | Adc::<T>::teardown_dma(); | 175 | Adc::<T>::stop(); |
| 176 | 176 | ||
| 177 | compiler_fence(Ordering::SeqCst); | 177 | compiler_fence(Ordering::SeqCst); |
| 178 | 178 | ||
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 97557ee8a..58c30935f 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -43,22 +43,22 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | #[cfg(not(adc_l0))] | 45 | #[cfg(not(adc_l0))] |
| 46 | impl super::VBatConverter for crate::peripherals::ADC1 { | 46 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 { |
| 47 | const CHANNEL: u8 = 18; | 47 | const CHANNEL: u8 = 18; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | #[cfg(not(adc_l0))] | 50 | #[cfg(not(adc_l0))] |
| 51 | impl super::VrefConverter for crate::peripherals::ADC1 { | 51 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 52 | const CHANNEL: u8 = 17; | 52 | const CHANNEL: u8 = 17; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | #[cfg(adc_l0)] | 55 | #[cfg(adc_l0)] |
| 56 | impl super::VrefConverter for crate::peripherals::ADC1 { | 56 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 57 | const CHANNEL: u8 = 18; | 57 | const CHANNEL: u8 = 18; |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | #[cfg(not(adc_l0))] | 60 | #[cfg(not(adc_l0))] |
| 61 | impl super::TemperatureConverter for crate::peripherals::ADC1 { | 61 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 62 | const CHANNEL: u8 = 16; | 62 | const CHANNEL: u8 = 16; |
| 63 | } | 63 | } |
| 64 | 64 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 88a8b96ed..2f9fabafb 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,15 +1,11 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 2 | ||
| 4 | use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; | 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; |
| 6 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 7 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 8 | use crate::{Peri, rcc}; | 7 | use crate::{Peri, rcc}; |
| 9 | 8 | ||
| 10 | mod ringbuffered; | ||
| 11 | pub use ringbuffered::RingBufferedAdc; | ||
| 12 | |||
| 13 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 9 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 14 | r.sr().modify(|regs| { | 10 | r.sr().modify(|regs| { |
| 15 | regs.set_eoc(false); | 11 | regs.set_eoc(false); |
| @@ -22,21 +18,21 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 22 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 18 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 23 | pub const VREF_CALIB_MV: u32 = 3300; | 19 | pub const VREF_CALIB_MV: u32 = 3300; |
| 24 | 20 | ||
| 25 | impl super::VrefConverter for crate::peripherals::ADC1 { | 21 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 26 | const CHANNEL: u8 = 17; | 22 | const CHANNEL: u8 = 17; |
| 27 | } | 23 | } |
| 28 | 24 | ||
| 29 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] | 25 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] |
| 30 | impl super::TemperatureConverter for crate::peripherals::ADC1 { | 26 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 31 | const CHANNEL: u8 = 16; | 27 | const CHANNEL: u8 = 16; |
| 32 | } | 28 | } |
| 33 | 29 | ||
| 34 | #[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))] | 30 | #[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))] |
| 35 | impl super::TemperatureConverter for crate::peripherals::ADC1 { | 31 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 36 | const CHANNEL: u8 = 18; | 32 | const CHANNEL: u8 = 18; |
| 37 | } | 33 | } |
| 38 | 34 | ||
| 39 | impl super::VBatConverter for crate::peripherals::ADC1 { | 35 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 { |
| 40 | const CHANNEL: u8 = 18; | 36 | const CHANNEL: u8 = 18; |
| 41 | } | 37 | } |
| 42 | 38 | ||
| @@ -89,11 +85,21 @@ impl Prescaler { | |||
| 89 | } | 85 | } |
| 90 | } | 86 | } |
| 91 | 87 | ||
| 88 | /// ADC configuration | ||
| 89 | #[derive(Default)] | ||
| 90 | pub struct AdcConfig { | ||
| 91 | resolution: Option<Resolution>, | ||
| 92 | } | ||
| 93 | |||
| 92 | impl<'d, T> Adc<'d, T> | 94 | impl<'d, T> Adc<'d, T> |
| 93 | where | 95 | where |
| 94 | T: Instance, | 96 | T: Instance, |
| 95 | { | 97 | { |
| 96 | pub fn new(adc: Peri<'d, T>) -> Self { | 98 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 99 | Self::new_with_config(adc, Default::default()) | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 97 | rcc::enable_and_reset::<T>(); | 103 | rcc::enable_and_reset::<T>(); |
| 98 | 104 | ||
| 99 | let presc = Prescaler::from_pclk2(T::frequency()); | 105 | let presc = Prescaler::from_pclk2(T::frequency()); |
| @@ -104,84 +110,14 @@ where | |||
| 104 | 110 | ||
| 105 | blocking_delay_us(3); | 111 | blocking_delay_us(3); |
| 106 | 112 | ||
| 107 | Self { adc } | 113 | if let Some(resolution) = config.resolution { |
| 108 | } | 114 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); |
| 109 | 115 | } | |
| 110 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 111 | /// | ||
| 112 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 113 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 114 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 115 | /// | ||
| 116 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 117 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 118 | /// | ||
| 119 | /// [`read`]: #method.read | ||
| 120 | pub fn into_ring_buffered<'a>( | ||
| 121 | self, | ||
| 122 | dma: Peri<'d, impl RxDma<T>>, | ||
| 123 | dma_buf: &'d mut [u16], | ||
| 124 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 125 | ) -> RingBufferedAdc<'d, T> { | ||
| 126 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 127 | |||
| 128 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 129 | channel.setup(); | ||
| 130 | |||
| 131 | (channel.channel, sample_time) | ||
| 132 | })); | ||
| 133 | compiler_fence(Ordering::SeqCst); | ||
| 134 | |||
| 135 | Self::setup_dma(); | ||
| 136 | |||
| 137 | // Don't disable the clock | ||
| 138 | mem::forget(self); | ||
| 139 | |||
| 140 | RingBufferedAdc::new(dma, dma_buf) | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 144 | channel.setup(); | ||
| 145 | |||
| 146 | // Configure ADC | ||
| 147 | let channel = channel.channel(); | ||
| 148 | |||
| 149 | Self::configure_sequence([(channel, sample_time)].into_iter()); | ||
| 150 | Self::blocking_convert() | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | ||
| 154 | /// [Adc::read_internal()] to perform conversion. | ||
| 155 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 156 | T::common_regs().ccr().modify(|reg| { | ||
| 157 | reg.set_tsvrefe(true); | ||
| 158 | }); | ||
| 159 | |||
| 160 | VrefInt {} | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Enables internal temperature sensor and returns [Temperature], which can be used in | ||
| 164 | /// [Adc::read_internal()] to perform conversion. | ||
| 165 | /// | ||
| 166 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 167 | /// temperature sensor will return vbat value. | ||
| 168 | pub fn enable_temperature(&self) -> Temperature { | ||
| 169 | T::common_regs().ccr().modify(|reg| { | ||
| 170 | reg.set_tsvrefe(true); | ||
| 171 | }); | ||
| 172 | 116 | ||
| 173 | Temperature {} | 117 | Self { adc } |
| 174 | } | 118 | } |
| 175 | 119 | ||
| 176 | /// Enables vbat input and returns [Vbat], which can be used in | 120 | pub(super) fn enable() {} |
| 177 | /// [Adc::read_internal()] to perform conversion. | ||
| 178 | pub fn enable_vbat(&self) -> Vbat { | ||
| 179 | T::common_regs().ccr().modify(|reg| { | ||
| 180 | reg.set_vbate(true); | ||
| 181 | }); | ||
| 182 | |||
| 183 | Vbat {} | ||
| 184 | } | ||
| 185 | 121 | ||
| 186 | pub(super) fn start() { | 122 | pub(super) fn start() { |
| 187 | // Begin ADC conversions | 123 | // Begin ADC conversions |
| @@ -192,18 +128,31 @@ where | |||
| 192 | } | 128 | } |
| 193 | 129 | ||
| 194 | pub(super) fn stop() { | 130 | pub(super) fn stop() { |
| 131 | let r = T::regs(); | ||
| 132 | |||
| 195 | // Stop ADC | 133 | // Stop ADC |
| 196 | T::regs().cr2().modify(|reg| { | 134 | r.cr2().modify(|reg| { |
| 197 | // Stop ADC | 135 | // Stop ADC |
| 198 | reg.set_swstart(false); | 136 | reg.set_swstart(false); |
| 137 | // Stop ADC | ||
| 138 | reg.set_adon(false); | ||
| 139 | // Stop DMA | ||
| 140 | reg.set_dma(false); | ||
| 199 | }); | 141 | }); |
| 200 | } | ||
| 201 | 142 | ||
| 202 | pub fn set_resolution(&mut self, resolution: Resolution) { | 143 | r.cr1().modify(|w| { |
| 203 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 144 | // Disable interrupt for end of conversion |
| 145 | w.set_eocie(false); | ||
| 146 | // Disable interrupt for overrun | ||
| 147 | w.set_ovrie(false); | ||
| 148 | }); | ||
| 149 | |||
| 150 | clear_interrupt_flags(r); | ||
| 151 | |||
| 152 | compiler_fence(Ordering::SeqCst); | ||
| 204 | } | 153 | } |
| 205 | 154 | ||
| 206 | pub(super) fn blocking_convert() -> u16 { | 155 | pub(super) fn convert() -> u16 { |
| 207 | // clear end of conversion flag | 156 | // clear end of conversion flag |
| 208 | T::regs().sr().modify(|reg| { | 157 | T::regs().sr().modify(|reg| { |
| 209 | reg.set_eoc(false); | 158 | reg.set_eoc(false); |
| @@ -224,7 +173,44 @@ where | |||
| 224 | T::regs().dr().read().0 as u16 | 173 | T::regs().dr().read().0 as u16 |
| 225 | } | 174 | } |
| 226 | 175 | ||
| 227 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | 176 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 177 | match conversion_mode { | ||
| 178 | ConversionMode::Repeated(_) => { | ||
| 179 | let r = T::regs(); | ||
| 180 | |||
| 181 | // Clear all interrupts | ||
| 182 | r.sr().modify(|regs| { | ||
| 183 | regs.set_eoc(false); | ||
| 184 | regs.set_ovr(false); | ||
| 185 | regs.set_strt(false); | ||
| 186 | }); | ||
| 187 | |||
| 188 | r.cr1().modify(|w| { | ||
| 189 | // Enable interrupt for end of conversion | ||
| 190 | w.set_eocie(true); | ||
| 191 | // Enable interrupt for overrun | ||
| 192 | w.set_ovrie(true); | ||
| 193 | // Scanning converisons of multiple channels | ||
| 194 | w.set_scan(true); | ||
| 195 | // Continuous conversion mode | ||
| 196 | w.set_discen(false); | ||
| 197 | }); | ||
| 198 | |||
| 199 | r.cr2().modify(|w| { | ||
| 200 | // Enable DMA mode | ||
| 201 | w.set_dma(true); | ||
| 202 | // Enable continuous conversions | ||
| 203 | w.set_cont(true); | ||
| 204 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 205 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 206 | // EOC flag is set at the end of each conversion. | ||
| 207 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 208 | }); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 228 | T::regs().cr2().modify(|reg| { | 214 | T::regs().cr2().modify(|reg| { |
| 229 | reg.set_adon(true); | 215 | reg.set_adon(true); |
| 230 | }); | 216 | }); |
| @@ -234,7 +220,7 @@ where | |||
| 234 | r.set_l((sequence.len() - 1).try_into().unwrap()); | 220 | r.set_l((sequence.len() - 1).try_into().unwrap()); |
| 235 | }); | 221 | }); |
| 236 | 222 | ||
| 237 | for (i, (ch, sample_time)) in sequence.enumerate() { | 223 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { |
| 238 | // Set the channel in the right sequence field. | 224 | // Set the channel in the right sequence field. |
| 239 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | 225 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); |
| 240 | 226 | ||
| @@ -247,62 +233,37 @@ where | |||
| 247 | } | 233 | } |
| 248 | } | 234 | } |
| 249 | 235 | ||
| 250 | pub(super) fn setup_dma() { | 236 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| 251 | let r = T::regs(); | 237 | /// [Adc::read_internal()] to perform conversion. |
| 252 | 238 | pub fn enable_vrefint(&self) -> VrefInt { | |
| 253 | // Clear all interrupts | 239 | T::common_regs().ccr().modify(|reg| { |
| 254 | r.sr().modify(|regs| { | 240 | reg.set_tsvrefe(true); |
| 255 | regs.set_eoc(false); | ||
| 256 | regs.set_ovr(false); | ||
| 257 | regs.set_strt(false); | ||
| 258 | }); | ||
| 259 | |||
| 260 | r.cr1().modify(|w| { | ||
| 261 | // Enable interrupt for end of conversion | ||
| 262 | w.set_eocie(true); | ||
| 263 | // Enable interrupt for overrun | ||
| 264 | w.set_ovrie(true); | ||
| 265 | // Scanning converisons of multiple channels | ||
| 266 | w.set_scan(true); | ||
| 267 | // Continuous conversion mode | ||
| 268 | w.set_discen(false); | ||
| 269 | }); | 241 | }); |
| 270 | 242 | ||
| 271 | r.cr2().modify(|w| { | 243 | VrefInt {} |
| 272 | // Enable DMA mode | ||
| 273 | w.set_dma(true); | ||
| 274 | // Enable continuous conversions | ||
| 275 | w.set_cont(true); | ||
| 276 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 277 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 278 | // EOC flag is set at the end of each conversion. | ||
| 279 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 280 | }); | ||
| 281 | } | 244 | } |
| 282 | 245 | ||
| 283 | pub(super) fn teardown_dma() { | 246 | /// Enables internal temperature sensor and returns [Temperature], which can be used in |
| 284 | let r = T::regs(); | 247 | /// [Adc::read_internal()] to perform conversion. |
| 285 | 248 | /// | |
| 286 | // Stop ADC | 249 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, |
| 287 | r.cr2().modify(|reg| { | 250 | /// temperature sensor will return vbat value. |
| 288 | // Stop ADC | 251 | pub fn enable_temperature(&self) -> Temperature { |
| 289 | reg.set_swstart(false); | 252 | T::common_regs().ccr().modify(|reg| { |
| 290 | // Stop ADC | 253 | reg.set_tsvrefe(true); |
| 291 | reg.set_adon(false); | ||
| 292 | // Stop DMA | ||
| 293 | reg.set_dma(false); | ||
| 294 | }); | 254 | }); |
| 295 | 255 | ||
| 296 | r.cr1().modify(|w| { | 256 | Temperature {} |
| 297 | // Disable interrupt for end of conversion | 257 | } |
| 298 | w.set_eocie(false); | ||
| 299 | // Disable interrupt for overrun | ||
| 300 | w.set_ovrie(false); | ||
| 301 | }); | ||
| 302 | 258 | ||
| 303 | clear_interrupt_flags(r); | 259 | /// Enables vbat input and returns [Vbat], which can be used in |
| 260 | /// [Adc::read_internal()] to perform conversion. | ||
| 261 | pub fn enable_vbat(&self) -> Vbat { | ||
| 262 | T::common_regs().ccr().modify(|reg| { | ||
| 263 | reg.set_vbate(true); | ||
| 264 | }); | ||
| 304 | 265 | ||
| 305 | compiler_fence(Ordering::SeqCst); | 266 | Vbat {} |
| 306 | } | 267 | } |
| 307 | } | 268 | } |
| 308 | 269 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index e816907d1..62b5043ee 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | #[cfg(adc_g0)] | 2 | #[cfg(adc_g0)] |
| 3 | use heapless::Vec; | 3 | use heapless::Vec; |
| 4 | use pac::adc::vals::Dmacfg; | ||
| 5 | #[cfg(adc_g0)] | 4 | #[cfg(adc_g0)] |
| 6 | use pac::adc::vals::{Ckmode, Smpsel}; | 5 | use pac::adc::vals::Ckmode; |
| 6 | use pac::adc::vals::Dmacfg; | ||
| 7 | #[cfg(adc_v3)] | 7 | #[cfg(adc_v3)] |
| 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; |
| 9 | #[cfg(adc_g0)] | 9 | #[cfg(adc_g0)] |
| @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | 12 | #[allow(unused_imports)] |
| 13 | use super::SealedAdcChannel; | 13 | use super::SealedAdcChannel; |
| 14 | use super::{ | 14 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, | 15 | use crate::adc::ConversionMode; |
| 16 | blocking_delay_us, | ||
| 17 | }; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | mod ringbuffered; | ||
| 21 | |||
| 22 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 23 | use ringbuffered::RingBufferedAdc; | ||
| 24 | |||
| 25 | use crate::dma::Transfer; | ||
| 26 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 27 | 17 | ||
| 28 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| @@ -36,60 +26,60 @@ pub const VREF_CALIB_MV: u32 = 3000; | |||
| 36 | const SAMPLE_TIMES_CAPACITY: usize = 2; | 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; |
| 37 | 27 | ||
| 38 | #[cfg(adc_g0)] | 28 | #[cfg(adc_g0)] |
| 39 | impl<T: Instance> super::VrefConverter for T { | 29 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 40 | const CHANNEL: u8 = 13; | 30 | const CHANNEL: u8 = 13; |
| 41 | } | 31 | } |
| 42 | #[cfg(any(adc_h5, adc_h7rs))] | 32 | #[cfg(any(adc_h5, adc_h7rs))] |
| 43 | impl<T: Instance> super::VrefConverter for T { | 33 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 44 | const CHANNEL: u8 = 17; | 34 | const CHANNEL: u8 = 17; |
| 45 | } | 35 | } |
| 46 | #[cfg(adc_u0)] | 36 | #[cfg(adc_u0)] |
| 47 | impl<T: Instance> super::VrefConverter for T { | 37 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 48 | const CHANNEL: u8 = 12; | 38 | const CHANNEL: u8 = 12; |
| 49 | } | 39 | } |
| 50 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] | 40 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 51 | impl<T: Instance> super::VrefConverter for T { | 41 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 52 | const CHANNEL: u8 = 0; | 42 | const CHANNEL: u8 = 0; |
| 53 | } | 43 | } |
| 54 | 44 | ||
| 55 | #[cfg(adc_g0)] | 45 | #[cfg(adc_g0)] |
| 56 | impl<T: Instance> super::TemperatureConverter for T { | 46 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 57 | const CHANNEL: u8 = 12; | 47 | const CHANNEL: u8 = 12; |
| 58 | } | 48 | } |
| 59 | #[cfg(any(adc_h5, adc_h7rs))] | 49 | #[cfg(any(adc_h5, adc_h7rs))] |
| 60 | impl<T: Instance> super::TemperatureConverter for T { | 50 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 61 | const CHANNEL: u8 = 16; | 51 | const CHANNEL: u8 = 16; |
| 62 | } | 52 | } |
| 63 | #[cfg(adc_u0)] | 53 | #[cfg(adc_u0)] |
| 64 | impl<T: Instance> super::TemperatureConverter for T { | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 65 | const CHANNEL: u8 = 11; | 55 | const CHANNEL: u8 = 11; |
| 66 | } | 56 | } |
| 67 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] | 57 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 68 | impl<T: Instance> super::TemperatureConverter for T { | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 69 | const CHANNEL: u8 = 17; | 59 | const CHANNEL: u8 = 17; |
| 70 | } | 60 | } |
| 71 | 61 | ||
| 72 | #[cfg(adc_g0)] | 62 | #[cfg(adc_g0)] |
| 73 | impl<T: Instance> super::VBatConverter for T { | 63 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 74 | const CHANNEL: u8 = 14; | 64 | const CHANNEL: u8 = 14; |
| 75 | } | 65 | } |
| 76 | #[cfg(any(adc_h5, adc_h7rs))] | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 77 | impl<T: Instance> super::VBatConverter for T { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 78 | const CHANNEL: u8 = 2; | 68 | const CHANNEL: u8 = 2; |
| 79 | } | 69 | } |
| 80 | #[cfg(adc_u0)] | 70 | #[cfg(adc_u0)] |
| 81 | impl<T: Instance> super::VBatConverter for T { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 82 | const CHANNEL: u8 = 13; | 72 | const CHANNEL: u8 = 13; |
| 83 | } | 73 | } |
| 84 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] | 74 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 85 | impl<T: Instance> super::VBatConverter for T { | 75 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 86 | const CHANNEL: u8 = 18; | 76 | const CHANNEL: u8 = 18; |
| 87 | } | 77 | } |
| 88 | 78 | ||
| 89 | cfg_if! { | 79 | cfg_if! { |
| 90 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 91 | pub struct VddCore; | 81 | pub struct VddCore; |
| 92 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 93 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 94 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 95 | 6 | 85 | 6 |
| @@ -101,7 +91,7 @@ cfg_if! { | |||
| 101 | cfg_if! { | 91 | cfg_if! { |
| 102 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 103 | pub struct DacOut; | 93 | pub struct DacOut; |
| 104 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 105 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 106 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 107 | 19 | 97 | 19 |
| @@ -145,6 +135,32 @@ pub enum Clock { | |||
| 145 | 135 | ||
| 146 | }} | 136 | }} |
| 147 | 137 | ||
| 138 | #[cfg(adc_u0)] | ||
| 139 | type Ovss = u8; | ||
| 140 | #[cfg(adc_u0)] | ||
| 141 | type Ovsr = u8; | ||
| 142 | #[cfg(adc_v3)] | ||
| 143 | type Ovss = OversamplingShift; | ||
| 144 | #[cfg(adc_v3)] | ||
| 145 | type Ovsr = OversamplingRatio; | ||
| 146 | |||
| 147 | /// Adc configuration | ||
| 148 | #[derive(Default)] | ||
| 149 | pub struct AdcConfig { | ||
| 150 | #[cfg(any(adc_u0, adc_g0, adc_v3))] | ||
| 151 | pub oversampling_shift: Option<Ovss>, | ||
| 152 | #[cfg(any(adc_u0, adc_g0, adc_v3))] | ||
| 153 | pub oversampling_ratio: Option<Ovsr>, | ||
| 154 | #[cfg(any(adc_u0, adc_g0))] | ||
| 155 | pub oversampling_enable: Option<bool>, | ||
| 156 | #[cfg(adc_v3)] | ||
| 157 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 158 | #[cfg(adc_g0)] | ||
| 159 | pub clock: Option<Clock>, | ||
| 160 | pub resolution: Option<Resolution>, | ||
| 161 | pub averaging: Option<Averaging>, | ||
| 162 | } | ||
| 163 | |||
| 148 | impl<'d, T: Instance> Adc<'d, T> { | 164 | impl<'d, T: Instance> Adc<'d, T> { |
| 149 | /// Enable the voltage regulator | 165 | /// Enable the voltage regulator |
| 150 | fn init_regulator() { | 166 | fn init_regulator() { |
| @@ -178,38 +194,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 178 | blocking_delay_us(1); | 194 | blocking_delay_us(1); |
| 179 | } | 195 | } |
| 180 | 196 | ||
| 181 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 182 | pub(super) fn start() { | ||
| 183 | // Start adc conversion | ||
| 184 | T::regs().cr().modify(|reg| { | ||
| 185 | reg.set_adstart(true); | ||
| 186 | }); | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 190 | pub(super) fn stop() { | ||
| 191 | // Stop adc conversion | ||
| 192 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 193 | T::regs().cr().modify(|reg| { | ||
| 194 | reg.set_adstp(true); | ||
| 195 | }); | ||
| 196 | while T::regs().cr().read().adstart() {} | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 201 | pub(super) fn teardown_dma() { | ||
| 202 | //disable dma control | ||
| 203 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 204 | T::regs().cfgr().modify(|reg| { | ||
| 205 | reg.set_dmaen(false); | ||
| 206 | }); | ||
| 207 | #[cfg(any(adc_g0, adc_u0))] | ||
| 208 | T::regs().cfgr1().modify(|reg| { | ||
| 209 | reg.set_dmaen(false); | ||
| 210 | }); | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Initialize the ADC leaving any analog clock at reset value. | 197 | /// Initialize the ADC leaving any analog clock at reset value. |
| 214 | /// For G0 and WL, this is the async clock without prescaler. | 198 | /// For G0 and WL, this is the async clock without prescaler. |
| 215 | pub fn new(adc: Peri<'d, T>) -> Self { | 199 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -218,6 +202,73 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 218 | Self { adc } | 202 | Self { adc } |
| 219 | } | 203 | } |
| 220 | 204 | ||
| 205 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 206 | #[cfg(not(adc_g0))] | ||
| 207 | let s = Self::new(adc); | ||
| 208 | |||
| 209 | #[cfg(adc_g0)] | ||
| 210 | let s = match config.clock { | ||
| 211 | Some(clock) => Self::new_with_clock(adc, clock), | ||
| 212 | None => Self::new(adc), | ||
| 213 | }; | ||
| 214 | |||
| 215 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 216 | if let Some(shift) = config.oversampling_shift { | ||
| 217 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 218 | } | ||
| 219 | |||
| 220 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 221 | if let Some(ratio) = config.oversampling_ratio { | ||
| 222 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 223 | } | ||
| 224 | |||
| 225 | #[cfg(any(adc_g0, adc_u0))] | ||
| 226 | if let Some(enable) = config.oversampling_enable { | ||
| 227 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 228 | } | ||
| 229 | |||
| 230 | #[cfg(adc_v3)] | ||
| 231 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 232 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 233 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 234 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 235 | } | ||
| 236 | |||
| 237 | if let Some(resolution) = config.resolution { | ||
| 238 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 239 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 240 | #[cfg(any(adc_g0, adc_u0))] | ||
| 241 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 242 | } | ||
| 243 | |||
| 244 | if let Some(averaging) = config.averaging { | ||
| 245 | let (enable, samples, right_shift) = match averaging { | ||
| 246 | Averaging::Disabled => (false, 0, 0), | ||
| 247 | Averaging::Samples2 => (true, 0, 1), | ||
| 248 | Averaging::Samples4 => (true, 1, 2), | ||
| 249 | Averaging::Samples8 => (true, 2, 3), | ||
| 250 | Averaging::Samples16 => (true, 3, 4), | ||
| 251 | Averaging::Samples32 => (true, 4, 5), | ||
| 252 | Averaging::Samples64 => (true, 5, 6), | ||
| 253 | Averaging::Samples128 => (true, 6, 7), | ||
| 254 | Averaging::Samples256 => (true, 7, 8), | ||
| 255 | }; | ||
| 256 | T::regs().cfgr2().modify(|reg| { | ||
| 257 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 258 | reg.set_rovse(enable); | ||
| 259 | #[cfg(any(adc_g0, adc_u0))] | ||
| 260 | reg.set_ovse(enable); | ||
| 261 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 262 | reg.set_ovsr(samples.into()); | ||
| 263 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 264 | reg.set_ovsr(samples.into()); | ||
| 265 | reg.set_ovss(right_shift.into()); | ||
| 266 | }) | ||
| 267 | } | ||
| 268 | |||
| 269 | s | ||
| 270 | } | ||
| 271 | |||
| 221 | #[cfg(adc_g0)] | 272 | #[cfg(adc_g0)] |
| 222 | /// Initialize ADC with explicit clock for the analog ADC | 273 | /// Initialize ADC with explicit clock for the analog ADC |
| 223 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | 274 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| @@ -255,7 +306,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 255 | } | 306 | } |
| 256 | 307 | ||
| 257 | // Enable ADC only when it is not already running. | 308 | // Enable ADC only when it is not already running. |
| 258 | fn enable(&mut self) { | 309 | pub(super) fn enable() { |
| 259 | // Make sure bits are off | 310 | // Make sure bits are off |
| 260 | while T::regs().cr().read().addis() { | 311 | while T::regs().cr().read().addis() { |
| 261 | // spin | 312 | // spin |
| @@ -276,258 +327,75 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 276 | } | 327 | } |
| 277 | } | 328 | } |
| 278 | 329 | ||
| 279 | pub fn enable_vrefint(&self) -> VrefInt { | 330 | pub(super) fn start() { |
| 280 | #[cfg(not(any(adc_g0, adc_u0)))] | 331 | #[cfg(any(adc_v3, adc_g0, adc_u0))] |
| 281 | T::common_regs().ccr().modify(|reg| { | 332 | { |
| 282 | reg.set_vrefen(true); | 333 | // Start adc conversion |
| 283 | }); | 334 | T::regs().cr().modify(|reg| { |
| 284 | #[cfg(any(adc_g0, adc_u0))] | 335 | reg.set_adstart(true); |
| 285 | T::regs().ccr().modify(|reg| { | 336 | }); |
| 286 | reg.set_vrefen(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 290 | // to stabilize the internal voltage reference. | ||
| 291 | blocking_delay_us(15); | ||
| 292 | |||
| 293 | VrefInt {} | ||
| 294 | } | ||
| 295 | |||
| 296 | pub fn enable_temperature(&self) -> Temperature { | ||
| 297 | cfg_if! { | ||
| 298 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 299 | T::regs().ccr().modify(|reg| { | ||
| 300 | reg.set_tsen(true); | ||
| 301 | }); | ||
| 302 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 303 | T::common_regs().ccr().modify(|reg| { | ||
| 304 | reg.set_tsen(true); | ||
| 305 | }); | ||
| 306 | } else { | ||
| 307 | T::common_regs().ccr().modify(|reg| { | ||
| 308 | reg.set_ch17sel(true); | ||
| 309 | }); | ||
| 310 | } | ||
| 311 | } | 337 | } |
| 312 | |||
| 313 | Temperature {} | ||
| 314 | } | 338 | } |
| 315 | 339 | ||
| 316 | pub fn enable_vbat(&self) -> Vbat { | 340 | pub(super) fn stop() { |
| 317 | cfg_if! { | 341 | #[cfg(any(adc_v3, adc_g0, adc_u0))] |
| 318 | if #[cfg(any(adc_g0, adc_u0))] { | 342 | { |
| 319 | T::regs().ccr().modify(|reg| { | 343 | // Ensure conversions are finished. |
| 320 | reg.set_vbaten(true); | 344 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 321 | }); | 345 | T::regs().cr().modify(|reg| { |
| 322 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 346 | reg.set_adstp(true); |
| 323 | T::common_regs().ccr().modify(|reg| { | ||
| 324 | reg.set_vbaten(true); | ||
| 325 | }); | ||
| 326 | } else { | ||
| 327 | T::common_regs().ccr().modify(|reg| { | ||
| 328 | reg.set_ch18sel(true); | ||
| 329 | }); | 347 | }); |
| 348 | while T::regs().cr().read().adstart() {} | ||
| 330 | } | 349 | } |
| 331 | } | ||
| 332 | |||
| 333 | Vbat {} | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Set the ADC resolution. | ||
| 337 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 338 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 339 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 340 | #[cfg(any(adc_g0, adc_u0))] | ||
| 341 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 342 | } | ||
| 343 | 350 | ||
| 344 | pub fn set_averaging(&mut self, averaging: Averaging) { | 351 | // Reset configuration. |
| 345 | let (enable, samples, right_shift) = match averaging { | ||
| 346 | Averaging::Disabled => (false, 0, 0), | ||
| 347 | Averaging::Samples2 => (true, 0, 1), | ||
| 348 | Averaging::Samples4 => (true, 1, 2), | ||
| 349 | Averaging::Samples8 => (true, 2, 3), | ||
| 350 | Averaging::Samples16 => (true, 3, 4), | ||
| 351 | Averaging::Samples32 => (true, 4, 5), | ||
| 352 | Averaging::Samples64 => (true, 5, 6), | ||
| 353 | Averaging::Samples128 => (true, 6, 7), | ||
| 354 | Averaging::Samples256 => (true, 7, 8), | ||
| 355 | }; | ||
| 356 | T::regs().cfgr2().modify(|reg| { | ||
| 357 | #[cfg(not(any(adc_g0, adc_u0)))] | 352 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 358 | reg.set_rovse(enable); | 353 | T::regs().cfgr().modify(|reg| { |
| 354 | reg.set_cont(false); | ||
| 355 | reg.set_dmaen(false); | ||
| 356 | }); | ||
| 359 | #[cfg(any(adc_g0, adc_u0))] | 357 | #[cfg(any(adc_g0, adc_u0))] |
| 360 | reg.set_ovse(enable); | 358 | T::regs().cfgr1().modify(|reg| { |
| 361 | #[cfg(any(adc_h5, adc_h7rs))] | 359 | reg.set_cont(false); |
| 362 | reg.set_ovsr(samples.into()); | 360 | reg.set_dmaen(false); |
| 363 | #[cfg(not(any(adc_h5, adc_h7rs)))] | 361 | }); |
| 364 | reg.set_ovsr(samples.into()); | ||
| 365 | reg.set_ovss(right_shift.into()); | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | /* | ||
| 369 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 370 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 371 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 372 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 373 | + 30.0 | ||
| 374 | } | ||
| 375 | */ | ||
| 376 | |||
| 377 | /// Perform a single conversion. | ||
| 378 | fn convert(&mut self) -> u16 { | ||
| 379 | T::regs().isr().modify(|reg| { | ||
| 380 | reg.set_eos(true); | ||
| 381 | reg.set_eoc(true); | ||
| 382 | }); | ||
| 383 | |||
| 384 | // Start conversion | ||
| 385 | T::regs().cr().modify(|reg| { | ||
| 386 | reg.set_adstart(true); | ||
| 387 | }); | ||
| 388 | |||
| 389 | while !T::regs().isr().read().eos() { | ||
| 390 | // spin | ||
| 391 | } | 362 | } |
| 392 | |||
| 393 | T::regs().dr().read().0 as u16 | ||
| 394 | } | 363 | } |
| 395 | 364 | ||
| 396 | /// Read an ADC channel. | 365 | /// Perform a single conversion. |
| 397 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 366 | pub(super) fn convert() -> u16 { |
| 398 | self.read_channel(channel, sample_time) | 367 | // Some models are affected by an erratum: |
| 399 | } | 368 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 400 | 369 | // corrupted, so we discard it and measure again. | |
| 401 | /// Read one or multiple ADC channels using DMA. | 370 | // |
| 402 | /// | 371 | // STM32L471xx: Section 2.7.3 |
| 403 | /// `readings` must have a length that is a multiple of the length of the | 372 | // STM32G4: Section 2.7.3 |
| 404 | /// `sequence` iterator. | 373 | #[cfg(any(rcc_l4, rcc_g4))] |
| 405 | /// | 374 | let len = 2; |
| 406 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 407 | /// channel number and not the pin order in `sequence`. | ||
| 408 | /// | ||
| 409 | /// Example | ||
| 410 | /// ```rust,ignore | ||
| 411 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 412 | /// | ||
| 413 | /// let mut adc = Adc::new(p.ADC1); | ||
| 414 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 415 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 416 | /// let mut measurements = [0u16; 2]; | ||
| 417 | /// | ||
| 418 | /// adc.read( | ||
| 419 | /// p.DMA1_CH2.reborrow(), | ||
| 420 | /// [ | ||
| 421 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 422 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 423 | /// ] | ||
| 424 | /// .into_iter(), | ||
| 425 | /// &mut measurements, | ||
| 426 | /// ) | ||
| 427 | /// .await; | ||
| 428 | /// defmt::info!("measurements: {}", measurements); | ||
| 429 | /// ``` | ||
| 430 | pub async fn read( | ||
| 431 | &mut self, | ||
| 432 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 433 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 434 | readings: &mut [u16], | ||
| 435 | ) { | ||
| 436 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 437 | assert!( | ||
| 438 | readings.len() % sequence.len() == 0, | ||
| 439 | "Readings length must be a multiple of sequence length" | ||
| 440 | ); | ||
| 441 | assert!( | ||
| 442 | sequence.len() <= 16, | ||
| 443 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 444 | ); | ||
| 445 | |||
| 446 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 447 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 448 | |||
| 449 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 450 | Self::cancel_conversions(); | ||
| 451 | self.enable(); | ||
| 452 | 375 | ||
| 453 | // Set sequence length | 376 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 454 | #[cfg(not(any(adc_g0, adc_u0)))] | 377 | let len = 1; |
| 455 | T::regs().sqr1().modify(|w| { | ||
| 456 | w.set_l(sequence.len() as u8 - 1); | ||
| 457 | }); | ||
| 458 | 378 | ||
| 459 | #[cfg(adc_g0)] | 379 | for _ in 0..len { |
| 460 | { | 380 | T::regs().isr().modify(|reg| { |
| 461 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 381 | reg.set_eos(true); |
| 462 | 382 | reg.set_eoc(true); | |
| 463 | T::regs().chselr().write(|chselr| { | ||
| 464 | T::regs().smpr().write(|smpr| { | ||
| 465 | for (channel, sample_time) in sequence { | ||
| 466 | chselr.set_chsel(channel.channel.into(), true); | ||
| 467 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 468 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 469 | } else { | ||
| 470 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 471 | if let Err(_) = sample_times.push(sample_time) { | ||
| 472 | panic!( | ||
| 473 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 474 | SAMPLE_TIMES_CAPACITY | ||
| 475 | ); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | }) | ||
| 480 | }); | 383 | }); |
| 481 | } | ||
| 482 | #[cfg(not(adc_g0))] | ||
| 483 | { | ||
| 484 | #[cfg(adc_u0)] | ||
| 485 | let mut channel_mask = 0; | ||
| 486 | |||
| 487 | // Configure channels and ranks | ||
| 488 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 489 | Self::configure_channel(channel, sample_time); | ||
| 490 | 384 | ||
| 491 | // Each channel is sampled according to sequence | 385 | // Start conversion |
| 492 | #[cfg(not(any(adc_g0, adc_u0)))] | 386 | T::regs().cr().modify(|reg| { |
| 493 | match _i { | 387 | reg.set_adstart(true); |
| 494 | 0..=3 => { | 388 | }); |
| 495 | T::regs().sqr1().modify(|w| { | ||
| 496 | w.set_sq(_i, channel.channel()); | ||
| 497 | }); | ||
| 498 | } | ||
| 499 | 4..=8 => { | ||
| 500 | T::regs().sqr2().modify(|w| { | ||
| 501 | w.set_sq(_i - 4, channel.channel()); | ||
| 502 | }); | ||
| 503 | } | ||
| 504 | 9..=13 => { | ||
| 505 | T::regs().sqr3().modify(|w| { | ||
| 506 | w.set_sq(_i - 9, channel.channel()); | ||
| 507 | }); | ||
| 508 | } | ||
| 509 | 14..=15 => { | ||
| 510 | T::regs().sqr4().modify(|w| { | ||
| 511 | w.set_sq(_i - 14, channel.channel()); | ||
| 512 | }); | ||
| 513 | } | ||
| 514 | _ => unreachable!(), | ||
| 515 | } | ||
| 516 | 389 | ||
| 517 | #[cfg(adc_u0)] | 390 | while !T::regs().isr().read().eos() { |
| 518 | { | 391 | // spin |
| 519 | channel_mask |= 1 << channel.channel(); | ||
| 520 | } | ||
| 521 | } | 392 | } |
| 522 | |||
| 523 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 524 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 525 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 526 | #[cfg(adc_u0)] | ||
| 527 | T::regs().chselr().modify(|reg| { | ||
| 528 | reg.set_chsel(channel_mask); | ||
| 529 | }); | ||
| 530 | } | 393 | } |
| 394 | |||
| 395 | T::regs().dr().read().0 as u16 | ||
| 396 | } | ||
| 397 | |||
| 398 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { | ||
| 531 | // Set continuous mode with oneshot dma. | 399 | // Set continuous mode with oneshot dma. |
| 532 | // Clear overrun flag before starting transfer. | 400 | // Clear overrun flag before starting transfer. |
| 533 | T::regs().isr().modify(|reg| { | 401 | T::regs().isr().modify(|reg| { |
| @@ -535,82 +403,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 535 | }); | 403 | }); |
| 536 | 404 | ||
| 537 | #[cfg(not(any(adc_g0, adc_u0)))] | 405 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 538 | T::regs().cfgr().modify(|reg| { | 406 | let regs = T::regs().cfgr(); |
| 539 | reg.set_discen(false); | 407 | |
| 540 | reg.set_cont(true); | ||
| 541 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 542 | reg.set_dmaen(true); | ||
| 543 | }); | ||
| 544 | #[cfg(any(adc_g0, adc_u0))] | 408 | #[cfg(any(adc_g0, adc_u0))] |
| 545 | T::regs().cfgr1().modify(|reg| { | 409 | let regs = T::regs().cfgr1(); |
| 410 | |||
| 411 | regs.modify(|reg| { | ||
| 546 | reg.set_discen(false); | 412 | reg.set_discen(false); |
| 547 | reg.set_cont(true); | 413 | reg.set_cont(true); |
| 548 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 414 | reg.set_dmacfg(match conversion_mode { |
| 415 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 416 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 417 | }); | ||
| 549 | reg.set_dmaen(true); | 418 | reg.set_dmaen(true); |
| 550 | }); | 419 | }); |
| 551 | |||
| 552 | let request = rx_dma.request(); | ||
| 553 | let transfer = unsafe { | ||
| 554 | Transfer::new_read( | ||
| 555 | rx_dma, | ||
| 556 | request, | ||
| 557 | T::regs().dr().as_ptr() as *mut u16, | ||
| 558 | readings, | ||
| 559 | Default::default(), | ||
| 560 | ) | ||
| 561 | }; | ||
| 562 | |||
| 563 | // Start conversion | ||
| 564 | T::regs().cr().modify(|reg| { | ||
| 565 | reg.set_adstart(true); | ||
| 566 | }); | ||
| 567 | |||
| 568 | // Wait for conversion sequence to finish. | ||
| 569 | transfer.await; | ||
| 570 | |||
| 571 | // Ensure conversions are finished. | ||
| 572 | Self::cancel_conversions(); | ||
| 573 | |||
| 574 | // Reset configuration. | ||
| 575 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 576 | T::regs().cfgr().modify(|reg| { | ||
| 577 | reg.set_cont(false); | ||
| 578 | }); | ||
| 579 | #[cfg(any(adc_g0, adc_u0))] | ||
| 580 | T::regs().cfgr1().modify(|reg| { | ||
| 581 | reg.set_cont(false); | ||
| 582 | }); | ||
| 583 | } | 420 | } |
| 584 | 421 | ||
| 585 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 422 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 586 | /// | ||
| 587 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 588 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 589 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 590 | /// | ||
| 591 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 592 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 593 | /// | ||
| 594 | /// [`read`]: #method.read | ||
| 595 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 596 | pub fn into_ring_buffered<'a>( | ||
| 597 | &mut self, | ||
| 598 | dma: Peri<'a, impl RxDma<T>>, | ||
| 599 | dma_buf: &'a mut [u16], | ||
| 600 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 601 | ) -> RingBufferedAdc<'a, T> { | ||
| 602 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 603 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 604 | assert!( | ||
| 605 | sequence.len() <= 16, | ||
| 606 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 607 | ); | ||
| 608 | // reset conversions and enable the adc | ||
| 609 | Self::cancel_conversions(); | ||
| 610 | self.enable(); | ||
| 611 | |||
| 612 | //adc side setup | ||
| 613 | |||
| 614 | // Set sequence length | 423 | // Set sequence length |
| 615 | #[cfg(not(any(adc_g0, adc_u0)))] | 424 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 616 | T::regs().sqr1().modify(|w| { | 425 | T::regs().sqr1().modify(|w| { |
| @@ -623,10 +432,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 623 | 432 | ||
| 624 | T::regs().chselr().write(|chselr| { | 433 | T::regs().chselr().write(|chselr| { |
| 625 | T::regs().smpr().write(|smpr| { | 434 | T::regs().smpr().write(|smpr| { |
| 626 | for (channel, sample_time) in sequence { | 435 | for ((channel, _), sample_time) in sequence { |
| 627 | chselr.set_chsel(channel.channel.into(), true); | 436 | chselr.set_chsel(channel.into(), true); |
| 628 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 437 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 629 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 438 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 630 | } else { | 439 | } else { |
| 631 | smpr.set_sample_time(sample_times.len(), sample_time); | 440 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 632 | if let Err(_) = sample_times.push(sample_time) { | 441 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -646,30 +455,63 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 646 | let mut channel_mask = 0; | 455 | let mut channel_mask = 0; |
| 647 | 456 | ||
| 648 | // Configure channels and ranks | 457 | // Configure channels and ranks |
| 649 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | 458 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 650 | Self::configure_channel(&mut channel, sample_time); | 459 | // RM0492, RM0481, etc. |
| 460 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 461 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 462 | if channel == 0 { | ||
| 463 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 464 | } | ||
| 465 | |||
| 466 | // Configure channel | ||
| 467 | cfg_if! { | ||
| 468 | if #[cfg(adc_u0)] { | ||
| 469 | // On G0 and U6 all channels use the same sampling time. | ||
| 470 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 471 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 472 | match channel { | ||
| 473 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 474 | _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 475 | } | ||
| 476 | } else { | ||
| 477 | let sample_time = sample_time.into(); | ||
| 478 | T::regs() | ||
| 479 | .smpr(channel as usize / 10) | ||
| 480 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | #[cfg(stm32h7)] | ||
| 485 | { | ||
| 486 | use crate::pac::adc::vals::Pcsel; | ||
| 487 | |||
| 488 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 489 | T::regs() | ||
| 490 | .pcsel() | ||
| 491 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 492 | } | ||
| 651 | 493 | ||
| 652 | // Each channel is sampled according to sequence | 494 | // Each channel is sampled according to sequence |
| 653 | #[cfg(not(any(adc_g0, adc_u0)))] | 495 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 654 | match _i { | 496 | match _i { |
| 655 | 0..=3 => { | 497 | 0..=3 => { |
| 656 | T::regs().sqr1().modify(|w| { | 498 | T::regs().sqr1().modify(|w| { |
| 657 | w.set_sq(_i, channel.channel()); | 499 | w.set_sq(_i, channel); |
| 658 | }); | 500 | }); |
| 659 | } | 501 | } |
| 660 | 4..=8 => { | 502 | 4..=8 => { |
| 661 | T::regs().sqr2().modify(|w| { | 503 | T::regs().sqr2().modify(|w| { |
| 662 | w.set_sq(_i - 4, channel.channel()); | 504 | w.set_sq(_i - 4, channel); |
| 663 | }); | 505 | }); |
| 664 | } | 506 | } |
| 665 | 9..=13 => { | 507 | 9..=13 => { |
| 666 | T::regs().sqr3().modify(|w| { | 508 | T::regs().sqr3().modify(|w| { |
| 667 | w.set_sq(_i - 9, channel.channel()); | 509 | w.set_sq(_i - 9, channel); |
| 668 | }); | 510 | }); |
| 669 | } | 511 | } |
| 670 | 14..=15 => { | 512 | 14..=15 => { |
| 671 | T::regs().sqr4().modify(|w| { | 513 | T::regs().sqr4().modify(|w| { |
| 672 | w.set_sq(_i - 14, channel.channel()); | 514 | w.set_sq(_i - 14, channel); |
| 673 | }); | 515 | }); |
| 674 | } | 516 | } |
| 675 | _ => unreachable!(), | 517 | _ => unreachable!(), |
| @@ -677,7 +519,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 677 | 519 | ||
| 678 | #[cfg(adc_u0)] | 520 | #[cfg(adc_u0)] |
| 679 | { | 521 | { |
| 680 | channel_mask |= 1 << channel.channel(); | 522 | channel_mask |= 1 << channel; |
| 681 | } | 523 | } |
| 682 | } | 524 | } |
| 683 | 525 | ||
| @@ -689,151 +531,71 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 689 | reg.set_chsel(channel_mask); | 531 | reg.set_chsel(channel_mask); |
| 690 | }); | 532 | }); |
| 691 | } | 533 | } |
| 692 | // Set continuous mode with Circular dma. | 534 | } |
| 693 | // Clear overrun flag before starting transfer. | ||
| 694 | T::regs().isr().modify(|reg| { | ||
| 695 | reg.set_ovr(true); | ||
| 696 | }); | ||
| 697 | 535 | ||
| 536 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 698 | #[cfg(not(any(adc_g0, adc_u0)))] | 537 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 699 | T::regs().cfgr().modify(|reg| { | 538 | T::common_regs().ccr().modify(|reg| { |
| 700 | reg.set_discen(false); | 539 | reg.set_vrefen(true); |
| 701 | reg.set_cont(true); | ||
| 702 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 703 | reg.set_dmaen(true); | ||
| 704 | }); | 540 | }); |
| 705 | #[cfg(any(adc_g0, adc_u0))] | 541 | #[cfg(any(adc_g0, adc_u0))] |
| 706 | T::regs().cfgr1().modify(|reg| { | 542 | T::regs().ccr().modify(|reg| { |
| 707 | reg.set_discen(false); | 543 | reg.set_vrefen(true); |
| 708 | reg.set_cont(true); | ||
| 709 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 710 | reg.set_dmaen(true); | ||
| 711 | }); | 544 | }); |
| 712 | 545 | ||
| 713 | RingBufferedAdc::new(dma, dma_buf) | 546 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us |
| 714 | } | 547 | // to stabilize the internal voltage reference. |
| 715 | 548 | blocking_delay_us(15); | |
| 716 | #[cfg(not(adc_g0))] | ||
| 717 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 718 | // RM0492, RM0481, etc. | ||
| 719 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 720 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 721 | if channel.channel() == 0 { | ||
| 722 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 723 | } | ||
| 724 | 549 | ||
| 725 | // Configure channel | 550 | VrefInt {} |
| 726 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 727 | } | 551 | } |
| 728 | 552 | ||
| 729 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 553 | pub fn enable_temperature(&self) -> Temperature { |
| 730 | self.enable(); | 554 | cfg_if! { |
| 731 | #[cfg(not(adc_g0))] | 555 | if #[cfg(any(adc_g0, adc_u0))] { |
| 732 | Self::configure_channel(channel, sample_time); | 556 | T::regs().ccr().modify(|reg| { |
| 733 | #[cfg(adc_g0)] | 557 | reg.set_tsen(true); |
| 734 | T::regs().smpr().write(|reg| { | 558 | }); |
| 735 | reg.set_sample_time(0, sample_time); | 559 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 736 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | 560 | T::common_regs().ccr().modify(|reg| { |
| 737 | }); | 561 | reg.set_tsen(true); |
| 738 | // Select channel | 562 | }); |
| 739 | #[cfg(not(any(adc_g0, adc_u0)))] | 563 | } else { |
| 740 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 564 | T::common_regs().ccr().modify(|reg| { |
| 741 | #[cfg(any(adc_g0, adc_u0))] | 565 | reg.set_ch17sel(true); |
| 742 | T::regs().chselr().write(|reg| { | 566 | }); |
| 743 | #[cfg(adc_g0)] | 567 | } |
| 744 | reg.set_chsel(channel.channel().into(), true); | ||
| 745 | #[cfg(adc_u0)] | ||
| 746 | reg.set_chsel(1 << channel.channel()); | ||
| 747 | }); | ||
| 748 | |||
| 749 | // Some models are affected by an erratum: | ||
| 750 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | ||
| 751 | // corrupted, so we discard it and measure again. | ||
| 752 | // | ||
| 753 | // STM32L471xx: Section 2.7.3 | ||
| 754 | // STM32G4: Section 2.7.3 | ||
| 755 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 756 | let _ = self.convert(); | ||
| 757 | let val = self.convert(); | ||
| 758 | |||
| 759 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 760 | |||
| 761 | // RM0492, RM0481, etc. | ||
| 762 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 763 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 764 | if channel.channel() == 0 { | ||
| 765 | T::regs().or().modify(|reg| reg.set_op0(false)); | ||
| 766 | } | 568 | } |
| 767 | 569 | ||
| 768 | val | 570 | Temperature {} |
| 769 | } | ||
| 770 | |||
| 771 | #[cfg(adc_g0)] | ||
| 772 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 773 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 774 | } | ||
| 775 | #[cfg(adc_u0)] | ||
| 776 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 777 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 778 | } | ||
| 779 | |||
| 780 | #[cfg(adc_g0)] | ||
| 781 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 782 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 783 | } | ||
| 784 | #[cfg(adc_u0)] | ||
| 785 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 786 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 787 | } | ||
| 788 | |||
| 789 | #[cfg(any(adc_g0, adc_u0))] | ||
| 790 | pub fn oversampling_enable(&mut self, enable: bool) { | ||
| 791 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 792 | } | ||
| 793 | |||
| 794 | #[cfg(adc_v3)] | ||
| 795 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 796 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 797 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 798 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 799 | } | ||
| 800 | |||
| 801 | #[cfg(adc_v3)] | ||
| 802 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | ||
| 803 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 804 | } | ||
| 805 | |||
| 806 | #[cfg(adc_v3)] | ||
| 807 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 808 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 809 | } | 571 | } |
| 810 | 572 | ||
| 811 | #[cfg(not(adc_g0))] | 573 | pub fn enable_vbat(&self) -> Vbat { |
| 812 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 813 | cfg_if! { | 574 | cfg_if! { |
| 814 | if #[cfg(adc_u0)] { | 575 | if #[cfg(any(adc_g0, adc_u0))] { |
| 815 | // On G0 and U6 all channels use the same sampling time. | 576 | T::regs().ccr().modify(|reg| { |
| 816 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 577 | reg.set_vbaten(true); |
| 578 | }); | ||
| 817 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 579 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 818 | match _ch { | 580 | T::common_regs().ccr().modify(|reg| { |
| 819 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 581 | reg.set_vbaten(true); |
| 820 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 582 | }); |
| 821 | } | ||
| 822 | } else { | 583 | } else { |
| 823 | let sample_time = sample_time.into(); | 584 | T::common_regs().ccr().modify(|reg| { |
| 824 | T::regs() | 585 | reg.set_ch18sel(true); |
| 825 | .smpr(_ch as usize / 10) | 586 | }); |
| 826 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 827 | } | 587 | } |
| 828 | } | 588 | } |
| 589 | |||
| 590 | Vbat {} | ||
| 829 | } | 591 | } |
| 830 | 592 | ||
| 831 | fn cancel_conversions() { | 593 | /* |
| 832 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 594 | /// Convert a raw sample from the `Temperature` to deg C |
| 833 | T::regs().cr().modify(|reg| { | 595 | pub fn to_degrees_centigrade(sample: u16) -> f32 { |
| 834 | reg.set_adstp(true); | 596 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) |
| 835 | }); | 597 | * (sample as f32 - VtempCal30::get().read() as f32) |
| 836 | while T::regs().cr().read().adstart() {} | 598 | + 30.0 |
| 837 | } | ||
| 838 | } | 599 | } |
| 600 | */ | ||
| 839 | } | 601 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 2f7baf3bf..9be6bcd0b 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,11 +4,8 @@ use pac::adc::vals::{Adcaldif, Boost}; | |||
| 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; |
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{ | 7 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, | 8 | use crate::adc::ConversionMode; |
| 9 | VrefInt, blocking_delay_us, | ||
| 10 | }; | ||
| 11 | use crate::dma::Transfer; | ||
| 12 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 13 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| 14 | 11 | ||
| @@ -26,39 +23,39 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | |||
| 26 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 27 | 24 | ||
| 28 | #[cfg(stm32g4)] | 25 | #[cfg(stm32g4)] |
| 29 | impl<T: Instance> super::VrefConverter for T { | 26 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 30 | const CHANNEL: u8 = 18; | 27 | const CHANNEL: u8 = 18; |
| 31 | } | 28 | } |
| 32 | #[cfg(stm32g4)] | 29 | #[cfg(stm32g4)] |
| 33 | impl<T: Instance> super::TemperatureConverter for T { | 30 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 34 | const CHANNEL: u8 = 16; | 31 | const CHANNEL: u8 = 16; |
| 35 | } | 32 | } |
| 36 | 33 | ||
| 37 | #[cfg(stm32h7)] | 34 | #[cfg(stm32h7)] |
| 38 | impl<T: Instance> super::VrefConverter for T { | 35 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 39 | const CHANNEL: u8 = 19; | 36 | const CHANNEL: u8 = 19; |
| 40 | } | 37 | } |
| 41 | #[cfg(stm32h7)] | 38 | #[cfg(stm32h7)] |
| 42 | impl<T: Instance> super::TemperatureConverter for T { | 39 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 43 | const CHANNEL: u8 = 18; | 40 | const CHANNEL: u8 = 18; |
| 44 | } | 41 | } |
| 45 | 42 | ||
| 46 | // TODO this should be 14 for H7a/b/35 | 43 | // TODO this should be 14 for H7a/b/35 |
| 47 | #[cfg(not(stm32u5))] | 44 | #[cfg(not(stm32u5))] |
| 48 | impl<T: Instance> super::VBatConverter for T { | 45 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 49 | const CHANNEL: u8 = 17; | 46 | const CHANNEL: u8 = 17; |
| 50 | } | 47 | } |
| 51 | 48 | ||
| 52 | #[cfg(stm32u5)] | 49 | #[cfg(stm32u5)] |
| 53 | impl<T: Instance> super::VrefConverter for T { | 50 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 54 | const CHANNEL: u8 = 0; | 51 | const CHANNEL: u8 = 0; |
| 55 | } | 52 | } |
| 56 | #[cfg(stm32u5)] | 53 | #[cfg(stm32u5)] |
| 57 | impl<T: Instance> super::TemperatureConverter for T { | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 58 | const CHANNEL: u8 = 19; | 55 | const CHANNEL: u8 = 19; |
| 59 | } | 56 | } |
| 60 | #[cfg(stm32u5)] | 57 | #[cfg(stm32u5)] |
| 61 | impl<T: Instance> super::VBatConverter for T { | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 62 | const CHANNEL: u8 = 18; | 59 | const CHANNEL: u8 = 18; |
| 63 | } | 60 | } |
| 64 | 61 | ||
| @@ -147,7 +144,48 @@ pub enum Averaging { | |||
| 147 | Samples1024, | 144 | Samples1024, |
| 148 | } | 145 | } |
| 149 | 146 | ||
| 147 | /// Adc configuration | ||
| 148 | #[derive(Default)] | ||
| 149 | pub struct AdcConfig { | ||
| 150 | pub resolution: Option<Resolution>, | ||
| 151 | pub averaging: Option<Averaging>, | ||
| 152 | } | ||
| 153 | |||
| 150 | impl<'d, T: Instance> Adc<'d, T> { | 154 | impl<'d, T: Instance> Adc<'d, T> { |
| 155 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 156 | let s = Self::new(adc); | ||
| 157 | |||
| 158 | // Set the ADC resolution. | ||
| 159 | if let Some(resolution) = config.resolution { | ||
| 160 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 161 | } | ||
| 162 | |||
| 163 | // Set hardware averaging. | ||
| 164 | if let Some(averaging) = config.averaging { | ||
| 165 | let (enable, samples, right_shift) = match averaging { | ||
| 166 | Averaging::Disabled => (false, 0, 0), | ||
| 167 | Averaging::Samples2 => (true, 1, 1), | ||
| 168 | Averaging::Samples4 => (true, 3, 2), | ||
| 169 | Averaging::Samples8 => (true, 7, 3), | ||
| 170 | Averaging::Samples16 => (true, 15, 4), | ||
| 171 | Averaging::Samples32 => (true, 31, 5), | ||
| 172 | Averaging::Samples64 => (true, 63, 6), | ||
| 173 | Averaging::Samples128 => (true, 127, 7), | ||
| 174 | Averaging::Samples256 => (true, 255, 8), | ||
| 175 | Averaging::Samples512 => (true, 511, 9), | ||
| 176 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 177 | }; | ||
| 178 | |||
| 179 | T::regs().cfgr2().modify(|reg| { | ||
| 180 | reg.set_rovse(enable); | ||
| 181 | reg.set_ovsr(samples); | ||
| 182 | reg.set_ovss(right_shift); | ||
| 183 | }) | ||
| 184 | } | ||
| 185 | |||
| 186 | s | ||
| 187 | } | ||
| 188 | |||
| 151 | /// Create a new ADC driver. | 189 | /// Create a new ADC driver. |
| 152 | pub fn new(adc: Peri<'d, T>) -> Self { | 190 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 153 | rcc::enable_and_reset::<T>(); | 191 | rcc::enable_and_reset::<T>(); |
| @@ -179,37 +217,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 179 | }; | 217 | }; |
| 180 | T::regs().cr().modify(|w| w.set_boost(boost)); | 218 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 181 | } | 219 | } |
| 182 | let mut s = Self { adc }; | ||
| 183 | s.power_up(); | ||
| 184 | s.configure_differential_inputs(); | ||
| 185 | |||
| 186 | s.calibrate(); | ||
| 187 | blocking_delay_us(1); | ||
| 188 | |||
| 189 | s.enable(); | ||
| 190 | s.configure(); | ||
| 191 | |||
| 192 | s | ||
| 193 | } | ||
| 194 | 220 | ||
| 195 | fn power_up(&mut self) { | ||
| 196 | T::regs().cr().modify(|reg| { | 221 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_deeppwd(false); | 222 | reg.set_deeppwd(false); |
| 198 | reg.set_advregen(true); | 223 | reg.set_advregen(true); |
| 199 | }); | 224 | }); |
| 200 | 225 | ||
| 201 | blocking_delay_us(10); | 226 | blocking_delay_us(10); |
| 202 | } | ||
| 203 | 227 | ||
| 204 | fn configure_differential_inputs(&mut self) { | ||
| 205 | T::regs().difsel().modify(|w| { | 228 | T::regs().difsel().modify(|w| { |
| 206 | for n in 0..20 { | 229 | for n in 0..20 { |
| 207 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 230 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 208 | } | 231 | } |
| 209 | }); | 232 | }); |
| 210 | } | ||
| 211 | 233 | ||
| 212 | fn calibrate(&mut self) { | ||
| 213 | T::regs().cr().modify(|w| { | 234 | T::regs().cr().modify(|w| { |
| 214 | #[cfg(not(adc_u5))] | 235 | #[cfg(not(adc_u5))] |
| 215 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 236 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -219,80 +240,50 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 219 | T::regs().cr().modify(|w| w.set_adcal(true)); | 240 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 220 | 241 | ||
| 221 | while T::regs().cr().read().adcal() {} | 242 | while T::regs().cr().read().adcal() {} |
| 222 | } | ||
| 223 | 243 | ||
| 224 | fn enable(&mut self) { | 244 | blocking_delay_us(1); |
| 225 | T::regs().isr().write(|w| w.set_adrdy(true)); | 245 | |
| 226 | T::regs().cr().modify(|w| w.set_aden(true)); | 246 | Self::enable(); |
| 227 | while !T::regs().isr().read().adrdy() {} | ||
| 228 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 229 | } | ||
| 230 | 247 | ||
| 231 | fn configure(&mut self) { | ||
| 232 | // single conversion mode, software trigger | 248 | // single conversion mode, software trigger |
| 233 | T::regs().cfgr().modify(|w| { | 249 | T::regs().cfgr().modify(|w| { |
| 234 | w.set_cont(false); | 250 | w.set_cont(false); |
| 235 | w.set_exten(Exten::DISABLED); | 251 | w.set_exten(Exten::DISABLED); |
| 236 | }); | 252 | }); |
| 237 | } | ||
| 238 | 253 | ||
| 239 | /// Enable reading the voltage reference internal channel. | 254 | Self { adc } |
| 240 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 241 | T::common_regs().ccr().modify(|reg| { | ||
| 242 | reg.set_vrefen(true); | ||
| 243 | }); | ||
| 244 | |||
| 245 | VrefInt {} | ||
| 246 | } | 255 | } |
| 247 | 256 | ||
| 248 | /// Enable reading the temperature internal channel. | 257 | pub(super) fn enable() { |
| 249 | pub fn enable_temperature(&self) -> Temperature { | 258 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 250 | T::common_regs().ccr().modify(|reg| { | 259 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 251 | reg.set_vsenseen(true); | 260 | while !T::regs().isr().read().adrdy() {} |
| 252 | }); | 261 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 253 | |||
| 254 | Temperature {} | ||
| 255 | } | 262 | } |
| 256 | 263 | ||
| 257 | /// Enable reading the vbat internal channel. | 264 | pub(super) fn start() { |
| 258 | pub fn enable_vbat(&self) -> Vbat { | 265 | // Start conversion |
| 259 | T::common_regs().ccr().modify(|reg| { | 266 | T::regs().cr().modify(|reg| { |
| 260 | reg.set_vbaten(true); | 267 | reg.set_adstart(true); |
| 261 | }); | 268 | }); |
| 262 | |||
| 263 | Vbat {} | ||
| 264 | } | 269 | } |
| 265 | 270 | ||
| 266 | /// Set the ADC resolution. | 271 | pub(super) fn stop() { |
| 267 | pub fn set_resolution(&mut self, resolution: Resolution) { | 272 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 268 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 273 | T::regs().cr().modify(|reg| { |
| 269 | } | 274 | reg.set_adstp(Adstp::STOP); |
| 275 | }); | ||
| 276 | while T::regs().cr().read().adstart() {} | ||
| 277 | } | ||
| 270 | 278 | ||
| 271 | /// Set hardware averaging. | 279 | // Reset configuration. |
| 272 | pub fn set_averaging(&mut self, averaging: Averaging) { | 280 | T::regs().cfgr().modify(|reg| { |
| 273 | let (enable, samples, right_shift) = match averaging { | 281 | reg.set_cont(false); |
| 274 | Averaging::Disabled => (false, 0, 0), | 282 | reg.set_dmngt(Dmngt::from_bits(0)); |
| 275 | Averaging::Samples2 => (true, 1, 1), | 283 | }); |
| 276 | Averaging::Samples4 => (true, 3, 2), | ||
| 277 | Averaging::Samples8 => (true, 7, 3), | ||
| 278 | Averaging::Samples16 => (true, 15, 4), | ||
| 279 | Averaging::Samples32 => (true, 31, 5), | ||
| 280 | Averaging::Samples64 => (true, 63, 6), | ||
| 281 | Averaging::Samples128 => (true, 127, 7), | ||
| 282 | Averaging::Samples256 => (true, 255, 8), | ||
| 283 | Averaging::Samples512 => (true, 511, 9), | ||
| 284 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 285 | }; | ||
| 286 | |||
| 287 | T::regs().cfgr2().modify(|reg| { | ||
| 288 | reg.set_rovse(enable); | ||
| 289 | reg.set_ovsr(samples); | ||
| 290 | reg.set_ovss(right_shift); | ||
| 291 | }) | ||
| 292 | } | 284 | } |
| 293 | 285 | ||
| 294 | /// Perform a single conversion. | 286 | pub(super) fn convert() -> u16 { |
| 295 | fn convert(&mut self) -> u16 { | ||
| 296 | T::regs().isr().modify(|reg| { | 287 | T::regs().isr().modify(|reg| { |
| 297 | reg.set_eos(true); | 288 | reg.set_eos(true); |
| 298 | reg.set_eoc(true); | 289 | reg.set_eoc(true); |
| @@ -310,170 +301,96 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 310 | T::regs().dr().read().0 as u16 | 301 | T::regs().dr().read().0 as u16 |
| 311 | } | 302 | } |
| 312 | 303 | ||
| 313 | /// Read an ADC channel. | 304 | pub(super) fn configure_dma(conversion_mode: ConversionMode) { |
| 314 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 305 | match conversion_mode { |
| 315 | self.read_channel(channel, sample_time) | 306 | ConversionMode::Singular => { |
| 307 | T::regs().isr().modify(|reg| { | ||
| 308 | reg.set_ovr(true); | ||
| 309 | }); | ||
| 310 | T::regs().cfgr().modify(|reg| { | ||
| 311 | reg.set_cont(true); | ||
| 312 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 313 | }); | ||
| 314 | } | ||
| 315 | _ => unreachable!(), | ||
| 316 | } | ||
| 316 | } | 317 | } |
| 317 | 318 | ||
| 318 | /// Read one or multiple ADC channels using DMA. | 319 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 319 | /// | ||
| 320 | /// `sequence` iterator and `readings` must have the same length. | ||
| 321 | /// | ||
| 322 | /// Example | ||
| 323 | /// ```rust,ignore | ||
| 324 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 325 | /// | ||
| 326 | /// let mut adc = Adc::new(p.ADC1); | ||
| 327 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 328 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 329 | /// let mut measurements = [0u16; 2]; | ||
| 330 | /// | ||
| 331 | /// adc.read( | ||
| 332 | /// p.DMA2_CH0.reborrow(), | ||
| 333 | /// [ | ||
| 334 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 335 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 336 | /// ] | ||
| 337 | /// .into_iter(), | ||
| 338 | /// &mut measurements, | ||
| 339 | /// ) | ||
| 340 | /// .await; | ||
| 341 | /// defmt::info!("measurements: {}", measurements); | ||
| 342 | /// ``` | ||
| 343 | pub async fn read( | ||
| 344 | &mut self, | ||
| 345 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 346 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 347 | readings: &mut [u16], | ||
| 348 | ) { | ||
| 349 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 350 | assert!( | ||
| 351 | sequence.len() == readings.len(), | ||
| 352 | "Sequence length must be equal to readings length" | ||
| 353 | ); | ||
| 354 | assert!( | ||
| 355 | sequence.len() <= 16, | ||
| 356 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 357 | ); | ||
| 358 | |||
| 359 | // Ensure no conversions are ongoing | ||
| 360 | Self::cancel_conversions(); | ||
| 361 | |||
| 362 | // Set sequence length | 320 | // Set sequence length |
| 363 | T::regs().sqr1().modify(|w| { | 321 | T::regs().sqr1().modify(|w| { |
| 364 | w.set_l(sequence.len() as u8 - 1); | 322 | w.set_l(sequence.len() as u8 - 1); |
| 365 | }); | 323 | }); |
| 366 | 324 | ||
| 367 | // Configure channels and ranks | 325 | // Configure channels and ranks |
| 368 | for (i, (channel, sample_time)) in sequence.enumerate() { | 326 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 369 | Self::configure_channel(channel, sample_time); | 327 | let sample_time = sample_time.into(); |
| 328 | if channel <= 9 { | ||
| 329 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); | ||
| 330 | } else { | ||
| 331 | T::regs() | ||
| 332 | .smpr(1) | ||
| 333 | .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); | ||
| 334 | } | ||
| 335 | |||
| 336 | #[cfg(any(stm32h7, stm32u5))] | ||
| 337 | { | ||
| 338 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 339 | T::regs() | ||
| 340 | .pcsel() | ||
| 341 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 342 | } | ||
| 343 | |||
| 370 | match i { | 344 | match i { |
| 371 | 0..=3 => { | 345 | 0..=3 => { |
| 372 | T::regs().sqr1().modify(|w| { | 346 | T::regs().sqr1().modify(|w| { |
| 373 | w.set_sq(i, channel.channel()); | 347 | w.set_sq(i, channel); |
| 374 | }); | 348 | }); |
| 375 | } | 349 | } |
| 376 | 4..=8 => { | 350 | 4..=8 => { |
| 377 | T::regs().sqr2().modify(|w| { | 351 | T::regs().sqr2().modify(|w| { |
| 378 | w.set_sq(i - 4, channel.channel()); | 352 | w.set_sq(i - 4, channel); |
| 379 | }); | 353 | }); |
| 380 | } | 354 | } |
| 381 | 9..=13 => { | 355 | 9..=13 => { |
| 382 | T::regs().sqr3().modify(|w| { | 356 | T::regs().sqr3().modify(|w| { |
| 383 | w.set_sq(i - 9, channel.channel()); | 357 | w.set_sq(i - 9, channel); |
| 384 | }); | 358 | }); |
| 385 | } | 359 | } |
| 386 | 14..=15 => { | 360 | 14..=15 => { |
| 387 | T::regs().sqr4().modify(|w| { | 361 | T::regs().sqr4().modify(|w| { |
| 388 | w.set_sq(i - 14, channel.channel()); | 362 | w.set_sq(i - 14, channel); |
| 389 | }); | 363 | }); |
| 390 | } | 364 | } |
| 391 | _ => unreachable!(), | 365 | _ => unreachable!(), |
| 392 | } | 366 | } |
| 393 | } | 367 | } |
| 394 | |||
| 395 | // Set continuous mode with oneshot dma. | ||
| 396 | // Clear overrun flag before starting transfer. | ||
| 397 | |||
| 398 | T::regs().isr().modify(|reg| { | ||
| 399 | reg.set_ovr(true); | ||
| 400 | }); | ||
| 401 | T::regs().cfgr().modify(|reg| { | ||
| 402 | reg.set_cont(true); | ||
| 403 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let request = rx_dma.request(); | ||
| 407 | let transfer = unsafe { | ||
| 408 | Transfer::new_read( | ||
| 409 | rx_dma, | ||
| 410 | request, | ||
| 411 | T::regs().dr().as_ptr() as *mut u16, | ||
| 412 | readings, | ||
| 413 | Default::default(), | ||
| 414 | ) | ||
| 415 | }; | ||
| 416 | |||
| 417 | // Start conversion | ||
| 418 | T::regs().cr().modify(|reg| { | ||
| 419 | reg.set_adstart(true); | ||
| 420 | }); | ||
| 421 | |||
| 422 | // Wait for conversion sequence to finish. | ||
| 423 | transfer.await; | ||
| 424 | |||
| 425 | // Ensure conversions are finished. | ||
| 426 | Self::cancel_conversions(); | ||
| 427 | |||
| 428 | // Reset configuration. | ||
| 429 | T::regs().cfgr().modify(|reg| { | ||
| 430 | reg.set_cont(false); | ||
| 431 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 432 | }); | ||
| 433 | } | 368 | } |
| 434 | 369 | ||
| 435 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 370 | /// Enable reading the voltage reference internal channel. |
| 436 | channel.setup(); | 371 | pub fn enable_vrefint(&self) -> VrefInt { |
| 437 | 372 | T::common_regs().ccr().modify(|reg| { | |
| 438 | let channel = channel.channel(); | 373 | reg.set_vrefen(true); |
| 439 | 374 | }); | |
| 440 | Self::set_channel_sample_time(channel, sample_time); | ||
| 441 | 375 | ||
| 442 | #[cfg(any(stm32h7, stm32u5))] | 376 | VrefInt {} |
| 443 | { | ||
| 444 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 445 | T::regs() | ||
| 446 | .pcsel() | ||
| 447 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 448 | } | ||
| 449 | } | 377 | } |
| 450 | 378 | ||
| 451 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 379 | /// Enable reading the temperature internal channel. |
| 452 | Self::configure_channel(channel, sample_time); | 380 | pub fn enable_temperature(&self) -> Temperature { |
| 453 | 381 | T::common_regs().ccr().modify(|reg| { | |
| 454 | T::regs().sqr1().modify(|reg| { | 382 | reg.set_vsenseen(true); |
| 455 | reg.set_sq(0, channel.channel()); | ||
| 456 | reg.set_l(0); | ||
| 457 | }); | 383 | }); |
| 458 | 384 | ||
| 459 | self.convert() | 385 | Temperature {} |
| 460 | } | 386 | } |
| 461 | 387 | ||
| 462 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 388 | /// Enable reading the vbat internal channel. |
| 463 | let sample_time = sample_time.into(); | 389 | pub fn enable_vbat(&self) -> Vbat { |
| 464 | if ch <= 9 { | 390 | T::common_regs().ccr().modify(|reg| { |
| 465 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | 391 | reg.set_vbaten(true); |
| 466 | } else { | 392 | }); |
| 467 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | 393 | ||
| 471 | fn cancel_conversions() { | 394 | Vbat {} |
| 472 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 473 | T::regs().cr().modify(|reg| { | ||
| 474 | reg.set_adstp(Adstp::STOP); | ||
| 475 | }); | ||
| 476 | while T::regs().cr().read().adstart() {} | ||
| 477 | } | ||
| 478 | } | 395 | } |
| 479 | } | 396 | } |
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 0a30bc24b..cfb1bf021 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md | |||
| @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add support for USB HID Boot Protocol Mode | ||
| 12 | |||
| 11 | ## 0.5.1 - 2025-08-26 | 13 | ## 0.5.1 - 2025-08-26 |
| 12 | 14 | ||
| 13 | ## 0.5.0 - 2025-07-16 | 15 | ## 0.5.0 - 2025-07-16 |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 182e1f83f..64e8fd59f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -15,8 +15,6 @@ use crate::types::InterfaceNumber; | |||
| 15 | use crate::{Builder, Handler}; | 15 | use crate::{Builder, Handler}; |
| 16 | 16 | ||
| 17 | const USB_CLASS_HID: u8 = 0x03; | 17 | const USB_CLASS_HID: u8 = 0x03; |
| 18 | const USB_SUBCLASS_NONE: u8 = 0x00; | ||
| 19 | const USB_PROTOCOL_NONE: u8 = 0x00; | ||
| 20 | 18 | ||
| 21 | // HID | 19 | // HID |
| 22 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; | 20 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; |
| @@ -31,6 +29,52 @@ const HID_REQ_SET_REPORT: u8 = 0x09; | |||
| 31 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; | 29 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; |
| 32 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | 30 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; |
| 33 | 31 | ||
| 32 | /// Get/Set Protocol mapping | ||
| 33 | /// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf> | ||
| 34 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 36 | #[repr(u8)] | ||
| 37 | pub enum HidProtocolMode { | ||
| 38 | /// Hid Boot Protocol Mode | ||
| 39 | Boot = 0, | ||
| 40 | /// Hid Report Protocol Mode | ||
| 41 | Report = 1, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl From<u8> for HidProtocolMode { | ||
| 45 | fn from(mode: u8) -> HidProtocolMode { | ||
| 46 | if mode == HidProtocolMode::Boot as u8 { | ||
| 47 | HidProtocolMode::Boot | ||
| 48 | } else { | ||
| 49 | HidProtocolMode::Report | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// USB HID interface subclass values. | ||
| 55 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 57 | #[repr(u8)] | ||
| 58 | pub enum HidSubclass { | ||
| 59 | /// No subclass, standard HID device. | ||
| 60 | No = 0, | ||
| 61 | /// Boot interface subclass, supports BIOS boot protocol. | ||
| 62 | Boot = 1, | ||
| 63 | } | ||
| 64 | |||
| 65 | /// USB HID protocol values. | ||
| 66 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 67 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 68 | #[repr(u8)] | ||
| 69 | pub enum HidBootProtocol { | ||
| 70 | /// No boot protocol. | ||
| 71 | None = 0, | ||
| 72 | /// Keyboard boot protocol. | ||
| 73 | Keyboard = 1, | ||
| 74 | /// Mouse boot protocol. | ||
| 75 | Mouse = 2, | ||
| 76 | } | ||
| 77 | |||
| 34 | /// Configuration for the HID class. | 78 | /// Configuration for the HID class. |
| 35 | pub struct Config<'d> { | 79 | pub struct Config<'d> { |
| 36 | /// HID report descriptor. | 80 | /// HID report descriptor. |
| @@ -48,6 +92,12 @@ pub struct Config<'d> { | |||
| 48 | 92 | ||
| 49 | /// Max packet size for both the IN and OUT endpoints. | 93 | /// Max packet size for both the IN and OUT endpoints. |
| 50 | pub max_packet_size: u16, | 94 | pub max_packet_size: u16, |
| 95 | |||
| 96 | /// The HID subclass of this interface | ||
| 97 | pub hid_subclass: HidSubclass, | ||
| 98 | |||
| 99 | /// The HID boot protocol of this interface | ||
| 100 | pub hid_boot_protocol: HidBootProtocol, | ||
| 51 | } | 101 | } |
| 52 | 102 | ||
| 53 | /// Report ID | 103 | /// Report ID |
| @@ -109,10 +159,15 @@ fn build<'d, D: Driver<'d>>( | |||
| 109 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { | 159 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { |
| 110 | let len = config.report_descriptor.len(); | 160 | let len = config.report_descriptor.len(); |
| 111 | 161 | ||
| 112 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | 162 | let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8); |
| 113 | let mut iface = func.interface(); | 163 | let mut iface = func.interface(); |
| 114 | let if_num = iface.interface_number(); | 164 | let if_num = iface.interface_number(); |
| 115 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); | 165 | let mut alt = iface.alt_setting( |
| 166 | USB_CLASS_HID, | ||
| 167 | config.hid_subclass as u8, | ||
| 168 | config.hid_boot_protocol as u8, | ||
| 169 | None, | ||
| 170 | ); | ||
| 116 | 171 | ||
| 117 | // HID descriptor | 172 | // HID descriptor |
| 118 | alt.descriptor( | 173 | alt.descriptor( |
| @@ -389,6 +444,23 @@ pub trait RequestHandler { | |||
| 389 | OutResponse::Rejected | 444 | OutResponse::Rejected |
| 390 | } | 445 | } |
| 391 | 446 | ||
| 447 | /// Gets the current hid protocol. | ||
| 448 | /// | ||
| 449 | /// Returns `Report` protocol by default. | ||
| 450 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 451 | HidProtocolMode::Report | ||
| 452 | } | ||
| 453 | |||
| 454 | /// Sets the current hid protocol to `protocol`. | ||
| 455 | /// | ||
| 456 | /// Accepts only `Report` protocol by default. | ||
| 457 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 458 | match protocol { | ||
| 459 | HidProtocolMode::Report => OutResponse::Accepted, | ||
| 460 | HidProtocolMode::Boot => OutResponse::Rejected, | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 392 | /// Get the idle rate for `id`. | 464 | /// Get the idle rate for `id`. |
| 393 | /// | 465 | /// |
| 394 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` | 466 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` |
| @@ -482,11 +554,14 @@ impl<'d> Handler for Control<'d> { | |||
| 482 | _ => Some(OutResponse::Rejected), | 554 | _ => Some(OutResponse::Rejected), |
| 483 | }, | 555 | }, |
| 484 | HID_REQ_SET_PROTOCOL => { | 556 | HID_REQ_SET_PROTOCOL => { |
| 485 | if req.value == 1 { | 557 | let hid_protocol = HidProtocolMode::from(req.value as u8); |
| 486 | Some(OutResponse::Accepted) | 558 | match (self.request_handler.as_mut(), hid_protocol) { |
| 487 | } else { | 559 | (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)), |
| 488 | warn!("HID Boot Protocol is unsupported."); | 560 | (None, HidProtocolMode::Report) => Some(OutResponse::Accepted), |
| 489 | Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol | 561 | (None, HidProtocolMode::Boot) => { |
| 562 | info!("Received request to switch to Boot protocol mode, but it is disabled by default."); | ||
| 563 | Some(OutResponse::Rejected) | ||
| 564 | } | ||
| 490 | } | 565 | } |
| 491 | } | 566 | } |
| 492 | _ => Some(OutResponse::Rejected), | 567 | _ => Some(OutResponse::Rejected), |
| @@ -539,8 +614,12 @@ impl<'d> Handler for Control<'d> { | |||
| 539 | } | 614 | } |
| 540 | } | 615 | } |
| 541 | HID_REQ_GET_PROTOCOL => { | 616 | HID_REQ_GET_PROTOCOL => { |
| 542 | // UNSUPPORTED: Boot Protocol | 617 | if let Some(request_handler) = self.request_handler.as_mut() { |
| 543 | buf[0] = 1; | 618 | buf[0] = request_handler.get_protocol() as u8; |
| 619 | } else { | ||
| 620 | // Return `Report` protocol mode by default | ||
| 621 | buf[0] = HidProtocolMode::Report as u8; | ||
| 622 | } | ||
| 544 | Some(InResponse::Accepted(&buf[0..1])) | 623 | Some(InResponse::Accepted(&buf[0..1])) |
| 545 | } | 624 | } |
| 546 | _ => Some(InResponse::Rejected), | 625 | _ => Some(InResponse::Rejected), |
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 1cd730503..7b7303526 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -13,7 +13,9 @@ use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | |||
| 13 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | 13 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; |
| 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 15 | use embassy_sync::signal::Signal; | 15 | use embassy_sync::signal::Signal; |
| 16 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 16 | use embassy_usb::class::hid::{ |
| 17 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 18 | }; | ||
| 17 | use embassy_usb::control::OutResponse; | 19 | use embassy_usb::control::OutResponse; |
| 18 | use embassy_usb::{Builder, Config, Handler}; | 20 | use embassy_usb::{Builder, Config, Handler}; |
| 19 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 21 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -26,6 +28,8 @@ bind_interrupts!(struct Irqs { | |||
| 26 | 28 | ||
| 27 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | 29 | static SUSPENDED: AtomicBool = AtomicBool::new(false); |
| 28 | 30 | ||
| 31 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 32 | |||
| 29 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 34 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_nrf::init(Default::default()); | 35 | let p = embassy_nrf::init(Default::default()); |
| @@ -45,6 +49,10 @@ async fn main(_spawner: Spawner) { | |||
| 45 | config.max_power = 100; | 49 | config.max_power = 100; |
| 46 | config.max_packet_size_0 = 64; | 50 | config.max_packet_size_0 = 64; |
| 47 | config.supports_remote_wakeup = true; | 51 | config.supports_remote_wakeup = true; |
| 52 | config.composite_with_iads = false; | ||
| 53 | config.device_class = 0; | ||
| 54 | config.device_sub_class = 0; | ||
| 55 | config.device_protocol = 0; | ||
| 48 | 56 | ||
| 49 | // Create embassy-usb DeviceBuilder using the driver and config. | 57 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 50 | // It needs some buffers for building the descriptors. | 58 | // It needs some buffers for building the descriptors. |
| @@ -74,6 +82,8 @@ async fn main(_spawner: Spawner) { | |||
| 74 | request_handler: None, | 82 | request_handler: None, |
| 75 | poll_ms: 60, | 83 | poll_ms: 60, |
| 76 | max_packet_size: 64, | 84 | max_packet_size: 64, |
| 85 | hid_subclass: HidSubclass::Boot, | ||
| 86 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 77 | }; | 87 | }; |
| 78 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 88 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 79 | 89 | ||
| @@ -106,6 +116,11 @@ async fn main(_spawner: Spawner) { | |||
| 106 | if SUSPENDED.load(Ordering::Acquire) { | 116 | if SUSPENDED.load(Ordering::Acquire) { |
| 107 | info!("Triggering remote wakeup"); | 117 | info!("Triggering remote wakeup"); |
| 108 | remote_wakeup.signal(()); | 118 | remote_wakeup.signal(()); |
| 119 | } else if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { | ||
| 120 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { | ||
| 121 | Ok(()) => {} | ||
| 122 | Err(e) => warn!("Failed to send boot report: {:?}", e), | ||
| 123 | }; | ||
| 109 | } else { | 124 | } else { |
| 110 | let report = KeyboardReport { | 125 | let report = KeyboardReport { |
| 111 | keycodes: [4, 0, 0, 0, 0, 0], | 126 | keycodes: [4, 0, 0, 0, 0, 0], |
| @@ -121,16 +136,23 @@ async fn main(_spawner: Spawner) { | |||
| 121 | 136 | ||
| 122 | button.wait_for_high().await; | 137 | button.wait_for_high().await; |
| 123 | info!("RELEASED"); | 138 | info!("RELEASED"); |
| 124 | let report = KeyboardReport { | 139 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 125 | keycodes: [0, 0, 0, 0, 0, 0], | 140 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 126 | leds: 0, | 141 | Ok(()) => {} |
| 127 | modifier: 0, | 142 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 128 | reserved: 0, | 143 | }; |
| 129 | }; | 144 | } else { |
| 130 | match writer.write_serialize(&report).await { | 145 | let report = KeyboardReport { |
| 131 | Ok(()) => {} | 146 | keycodes: [0, 0, 0, 0, 0, 0], |
| 132 | Err(e) => warn!("Failed to send report: {:?}", e), | 147 | leds: 0, |
| 133 | }; | 148 | modifier: 0, |
| 149 | reserved: 0, | ||
| 150 | }; | ||
| 151 | match writer.write_serialize(&report).await { | ||
| 152 | Ok(()) => {} | ||
| 153 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 154 | }; | ||
| 155 | } | ||
| 134 | } | 156 | } |
| 135 | }; | 157 | }; |
| 136 | 158 | ||
| @@ -156,6 +178,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 156 | OutResponse::Accepted | 178 | OutResponse::Accepted |
| 157 | } | 179 | } |
| 158 | 180 | ||
| 181 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 182 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 183 | info!("The current HID protocol mode is: {}", protocol); | ||
| 184 | protocol | ||
| 185 | } | ||
| 186 | |||
| 187 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 188 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 189 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 190 | OutResponse::Accepted | ||
| 191 | } | ||
| 192 | |||
| 159 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 193 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 160 | info!("Set idle rate for {:?} to {:?}", id, dur); | 194 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 161 | } | 195 | } |
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 3c0fc04e8..6bee4546b 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -8,7 +10,9 @@ use embassy_nrf::usb::Driver; | |||
| 8 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | 10 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 9 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; | 11 | use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; |
| 10 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 11 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 12 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 13 | use embassy_usb::{Builder, Config}; | 17 | use embassy_usb::{Builder, Config}; |
| 14 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| @@ -19,6 +23,8 @@ bind_interrupts!(struct Irqs { | |||
| 19 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; | 23 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 20 | }); | 24 | }); |
| 21 | 25 | ||
| 26 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 27 | |||
| 22 | #[embassy_executor::main] | 28 | #[embassy_executor::main] |
| 23 | async fn main(_spawner: Spawner) { | 29 | async fn main(_spawner: Spawner) { |
| 24 | let p = embassy_nrf::init(Default::default()); | 30 | let p = embassy_nrf::init(Default::default()); |
| @@ -37,6 +43,10 @@ async fn main(_spawner: Spawner) { | |||
| 37 | config.serial_number = Some("12345678"); | 43 | config.serial_number = Some("12345678"); |
| 38 | config.max_power = 100; | 44 | config.max_power = 100; |
| 39 | config.max_packet_size_0 = 64; | 45 | config.max_packet_size_0 = 64; |
| 46 | config.composite_with_iads = false; | ||
| 47 | config.device_class = 0; | ||
| 48 | config.device_sub_class = 0; | ||
| 49 | config.device_protocol = 0; | ||
| 40 | 50 | ||
| 41 | // Create embassy-usb DeviceBuilder using the driver and config. | 51 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 42 | // It needs some buffers for building the descriptors. | 52 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +73,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: Some(&mut request_handler), | 73 | request_handler: Some(&mut request_handler), |
| 64 | poll_ms: 60, | 74 | poll_ms: 60, |
| 65 | max_packet_size: 8, | 75 | max_packet_size: 8, |
| 76 | hid_subclass: HidSubclass::Boot, | ||
| 77 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 66 | }; | 78 | }; |
| 67 | 79 | ||
| 68 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 80 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -80,16 +92,26 @@ async fn main(_spawner: Spawner) { | |||
| 80 | Timer::after_millis(500).await; | 92 | Timer::after_millis(500).await; |
| 81 | 93 | ||
| 82 | y = -y; | 94 | y = -y; |
| 83 | let report = MouseReport { | 95 | |
| 84 | buttons: 0, | 96 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 85 | x: 0, | 97 | let buttons = 0u8; |
| 86 | y, | 98 | let x = 0i8; |
| 87 | wheel: 0, | 99 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 88 | pan: 0, | 100 | Ok(()) => {} |
| 89 | }; | 101 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 90 | match writer.write_serialize(&report).await { | 102 | } |
| 91 | Ok(()) => {} | 103 | } else { |
| 92 | Err(e) => warn!("Failed to send report: {:?}", e), | 104 | let report = MouseReport { |
| 105 | buttons: 0, | ||
| 106 | x: 0, | ||
| 107 | y, | ||
| 108 | wheel: 0, | ||
| 109 | pan: 0, | ||
| 110 | }; | ||
| 111 | match writer.write_serialize(&report).await { | ||
| 112 | Ok(()) => {} | ||
| 113 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 114 | } | ||
| 93 | } | 115 | } |
| 94 | } | 116 | } |
| 95 | }; | 117 | }; |
| @@ -112,6 +134,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 112 | OutResponse::Accepted | 134 | OutResponse::Accepted |
| 113 | } | 135 | } |
| 114 | 136 | ||
| 137 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 138 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 139 | info!("The current HID protocol mode is: {}", protocol); | ||
| 140 | protocol | ||
| 141 | } | ||
| 142 | |||
| 143 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 144 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 145 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 146 | OutResponse::Accepted | ||
| 147 | } | ||
| 148 | |||
| 115 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 149 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 116 | info!("Set idle rate for {:?} to {:?}", id, dur); | 150 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 117 | } | 151 | } |
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index a7cb322d8..2f6d169bf 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; | |||
| 10 | use embassy_rp::gpio::{Input, Pull}; | 10 | use embassy_rp::gpio::{Input, Pull}; |
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| 13 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 14 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 15 | use embassy_usb::{Builder, Config, Handler}; | 17 | use embassy_usb::{Builder, Config, Handler}; |
| 16 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | 22 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 21 | }); | 23 | }); |
| 22 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 23 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 24 | async fn main(_spawner: Spawner) { | 28 | async fn main(_spawner: Spawner) { |
| 25 | let p = embassy_rp::init(Default::default()); | 29 | let p = embassy_rp::init(Default::default()); |
| @@ -33,6 +37,10 @@ async fn main(_spawner: Spawner) { | |||
| 33 | config.serial_number = Some("12345678"); | 37 | config.serial_number = Some("12345678"); |
| 34 | config.max_power = 100; | 38 | config.max_power = 100; |
| 35 | config.max_packet_size_0 = 64; | 39 | config.max_packet_size_0 = 64; |
| 40 | config.composite_with_iads = false; | ||
| 41 | config.device_class = 0; | ||
| 42 | config.device_sub_class = 0; | ||
| 43 | config.device_protocol = 0; | ||
| 36 | 44 | ||
| 37 | // Create embassy-usb DeviceBuilder using the driver and config. | 45 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 38 | // It needs some buffers for building the descriptors. | 46 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +71,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: None, | 71 | request_handler: None, |
| 64 | poll_ms: 60, | 72 | poll_ms: 60, |
| 65 | max_packet_size: 64, | 73 | max_packet_size: 64, |
| 74 | hid_subclass: HidSubclass::Boot, | ||
| 75 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 66 | }; | 76 | }; |
| 67 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 77 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 68 | 78 | ||
| @@ -86,30 +96,46 @@ async fn main(_spawner: Spawner) { | |||
| 86 | info!("Waiting for HIGH on pin 16"); | 96 | info!("Waiting for HIGH on pin 16"); |
| 87 | signal_pin.wait_for_high().await; | 97 | signal_pin.wait_for_high().await; |
| 88 | info!("HIGH DETECTED"); | 98 | info!("HIGH DETECTED"); |
| 89 | // Create a report with the A key pressed. (no shift modifier) | 99 | |
| 90 | let report = KeyboardReport { | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | 101 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 92 | leds: 0, | 102 | Ok(()) => {} |
| 93 | modifier: 0, | 103 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 94 | reserved: 0, | 104 | }; |
| 95 | }; | 105 | } else { |
| 96 | // Send the report. | 106 | // Create a report with the A key pressed. (no shift modifier) |
| 97 | match writer.write_serialize(&report).await { | 107 | let report = KeyboardReport { |
| 98 | Ok(()) => {} | 108 | keycodes: [4, 0, 0, 0, 0, 0], |
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | 109 | leds: 0, |
| 100 | }; | 110 | modifier: 0, |
| 111 | reserved: 0, | ||
| 112 | }; | ||
| 113 | // Send the report. | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 119 | |||
| 101 | signal_pin.wait_for_low().await; | 120 | signal_pin.wait_for_low().await; |
| 102 | info!("LOW DETECTED"); | 121 | info!("LOW DETECTED"); |
| 103 | let report = KeyboardReport { | 122 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | 123 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 105 | leds: 0, | 124 | Ok(()) => {} |
| 106 | modifier: 0, | 125 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 107 | reserved: 0, | 126 | }; |
| 108 | }; | 127 | } else { |
| 109 | match writer.write_serialize(&report).await { | 128 | let report = KeyboardReport { |
| 110 | Ok(()) => {} | 129 | keycodes: [0, 0, 0, 0, 0, 0], |
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | 130 | leds: 0, |
| 112 | }; | 131 | modifier: 0, |
| 132 | reserved: 0, | ||
| 133 | }; | ||
| 134 | match writer.write_serialize(&report).await { | ||
| 135 | Ok(()) => {} | ||
| 136 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 137 | }; | ||
| 138 | } | ||
| 113 | } | 139 | } |
| 114 | }; | 140 | }; |
| 115 | 141 | ||
| @@ -135,6 +161,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 135 | OutResponse::Accepted | 161 | OutResponse::Accepted |
| 136 | } | 162 | } |
| 137 | 163 | ||
| 164 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 165 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 166 | info!("The current HID protocol mode is: {}", protocol); | ||
| 167 | protocol | ||
| 168 | } | ||
| 169 | |||
| 170 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 171 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 172 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 173 | OutResponse::Accepted | ||
| 174 | } | ||
| 175 | |||
| 138 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 176 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | 177 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 140 | } | 178 | } |
diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 4454c593c..dc331cbdd 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -11,7 +11,9 @@ use embassy_rp::clocks::RoscRng; | |||
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 15 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 16 | use embassy_usb::{Builder, Config, Handler}; | 18 | use embassy_usb::{Builder, Config, Handler}; |
| 17 | use rand::Rng; | 19 | use rand::Rng; |
| @@ -22,6 +24,8 @@ bind_interrupts!(struct Irqs { | |||
| 22 | USBCTRL_IRQ => InterruptHandler<USB>; | 24 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 23 | }); | 25 | }); |
| 24 | 26 | ||
| 27 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 28 | |||
| 25 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| 26 | async fn main(_spawner: Spawner) { | 30 | async fn main(_spawner: Spawner) { |
| 27 | let p = embassy_rp::init(Default::default()); | 31 | let p = embassy_rp::init(Default::default()); |
| @@ -35,6 +39,10 @@ async fn main(_spawner: Spawner) { | |||
| 35 | config.serial_number = Some("12345678"); | 39 | config.serial_number = Some("12345678"); |
| 36 | config.max_power = 100; | 40 | config.max_power = 100; |
| 37 | config.max_packet_size_0 = 64; | 41 | config.max_packet_size_0 = 64; |
| 42 | config.composite_with_iads = false; | ||
| 43 | config.device_class = 0; | ||
| 44 | config.device_sub_class = 0; | ||
| 45 | config.device_protocol = 0; | ||
| 38 | 46 | ||
| 39 | // Create embassy-usb DeviceBuilder using the driver and config. | 47 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 40 | // It needs some buffers for building the descriptors. | 48 | // It needs some buffers for building the descriptors. |
| @@ -65,6 +73,8 @@ async fn main(_spawner: Spawner) { | |||
| 65 | request_handler: None, | 73 | request_handler: None, |
| 66 | poll_ms: 60, | 74 | poll_ms: 60, |
| 67 | max_packet_size: 64, | 75 | max_packet_size: 64, |
| 76 | hid_subclass: HidSubclass::Boot, | ||
| 77 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 68 | }; | 78 | }; |
| 69 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 79 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 70 | 80 | ||
| @@ -83,17 +93,29 @@ async fn main(_spawner: Spawner) { | |||
| 83 | loop { | 93 | loop { |
| 84 | // every 1 second | 94 | // every 1 second |
| 85 | _ = Timer::after_secs(1).await; | 95 | _ = Timer::after_secs(1).await; |
| 86 | let report = MouseReport { | 96 | |
| 87 | buttons: 0, | 97 | let x = rng.random_range(-100..100); // random small x movement |
| 88 | x: rng.random_range(-100..100), // random small x movement | 98 | let y = rng.random_range(-100..100); // random small y movement |
| 89 | y: rng.random_range(-100..100), // random small y movement | 99 | |
| 90 | wheel: 0, | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | pan: 0, | 101 | let buttons = 0u8; |
| 92 | }; | 102 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 93 | // Send the report. | 103 | Ok(()) => {} |
| 94 | match writer.write_serialize(&report).await { | 104 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 95 | Ok(()) => {} | 105 | } |
| 96 | Err(e) => warn!("Failed to send report: {:?}", e), | 106 | } else { |
| 107 | let report = MouseReport { | ||
| 108 | buttons: 0, | ||
| 109 | x, | ||
| 110 | y, | ||
| 111 | wheel: 0, | ||
| 112 | pan: 0, | ||
| 113 | }; | ||
| 114 | // Send the report. | ||
| 115 | match writer.write_serialize(&report).await { | ||
| 116 | Ok(()) => {} | ||
| 117 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 118 | } | ||
| 97 | } | 119 | } |
| 98 | } | 120 | } |
| 99 | }; | 121 | }; |
| @@ -120,6 +142,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 120 | OutResponse::Accepted | 142 | OutResponse::Accepted |
| 121 | } | 143 | } |
| 122 | 144 | ||
| 145 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 146 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 147 | info!("The current HID protocol mode is: {}", protocol); | ||
| 148 | protocol | ||
| 149 | } | ||
| 150 | |||
| 151 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 152 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 153 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 154 | OutResponse::Accepted | ||
| 155 | } | ||
| 156 | |||
| 123 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 157 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 124 | info!("Set idle rate for {:?} to {:?}", id, dur); | 158 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 125 | } | 159 | } |
diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index 6f496e23a..d8f64c470 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; | |||
| 10 | use embassy_rp::gpio::{Input, Pull}; | 10 | use embassy_rp::gpio::{Input, Pull}; |
| 11 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 12 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; |
| 13 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, | ||
| 15 | }; | ||
| 14 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 15 | use embassy_usb::{Builder, Config, Handler}; | 17 | use embassy_usb::{Builder, Config, Handler}; |
| 16 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | 22 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 21 | }); | 23 | }); |
| 22 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 23 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 24 | async fn main(_spawner: Spawner) { | 28 | async fn main(_spawner: Spawner) { |
| 25 | let p = embassy_rp::init(Default::default()); | 29 | let p = embassy_rp::init(Default::default()); |
| @@ -33,6 +37,10 @@ async fn main(_spawner: Spawner) { | |||
| 33 | config.serial_number = Some("12345678"); | 37 | config.serial_number = Some("12345678"); |
| 34 | config.max_power = 100; | 38 | config.max_power = 100; |
| 35 | config.max_packet_size_0 = 64; | 39 | config.max_packet_size_0 = 64; |
| 40 | config.composite_with_iads = false; | ||
| 41 | config.device_class = 0; | ||
| 42 | config.device_sub_class = 0; | ||
| 43 | config.device_protocol = 0; | ||
| 36 | 44 | ||
| 37 | // Create embassy-usb DeviceBuilder using the driver and config. | 45 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 38 | // It needs some buffers for building the descriptors. | 46 | // It needs some buffers for building the descriptors. |
| @@ -63,6 +71,8 @@ async fn main(_spawner: Spawner) { | |||
| 63 | request_handler: None, | 71 | request_handler: None, |
| 64 | poll_ms: 60, | 72 | poll_ms: 60, |
| 65 | max_packet_size: 64, | 73 | max_packet_size: 64, |
| 74 | hid_subclass: HidSubclass::Boot, | ||
| 75 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 66 | }; | 76 | }; |
| 67 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 77 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| 68 | 78 | ||
| @@ -86,30 +96,45 @@ async fn main(_spawner: Spawner) { | |||
| 86 | info!("Waiting for HIGH on pin 16"); | 96 | info!("Waiting for HIGH on pin 16"); |
| 87 | signal_pin.wait_for_high().await; | 97 | signal_pin.wait_for_high().await; |
| 88 | info!("HIGH DETECTED"); | 98 | info!("HIGH DETECTED"); |
| 89 | // Create a report with the A key pressed. (no shift modifier) | 99 | |
| 90 | let report = KeyboardReport { | 100 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | 101 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 92 | leds: 0, | 102 | Ok(()) => {} |
| 93 | modifier: 0, | 103 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 94 | reserved: 0, | 104 | }; |
| 95 | }; | 105 | } else { |
| 96 | // Send the report. | 106 | // Create a report with the A key pressed. (no shift modifier) |
| 97 | match writer.write_serialize(&report).await { | 107 | let report = KeyboardReport { |
| 98 | Ok(()) => {} | 108 | keycodes: [4, 0, 0, 0, 0, 0], |
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | 109 | leds: 0, |
| 100 | }; | 110 | modifier: 0, |
| 111 | reserved: 0, | ||
| 112 | }; | ||
| 113 | // Send the report. | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 101 | signal_pin.wait_for_low().await; | 119 | signal_pin.wait_for_low().await; |
| 102 | info!("LOW DETECTED"); | 120 | info!("LOW DETECTED"); |
| 103 | let report = KeyboardReport { | 121 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | 122 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 105 | leds: 0, | 123 | Ok(()) => {} |
| 106 | modifier: 0, | 124 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 107 | reserved: 0, | 125 | }; |
| 108 | }; | 126 | } else { |
| 109 | match writer.write_serialize(&report).await { | 127 | let report = KeyboardReport { |
| 110 | Ok(()) => {} | 128 | keycodes: [0, 0, 0, 0, 0, 0], |
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | 129 | leds: 0, |
| 112 | }; | 130 | modifier: 0, |
| 131 | reserved: 0, | ||
| 132 | }; | ||
| 133 | match writer.write_serialize(&report).await { | ||
| 134 | Ok(()) => {} | ||
| 135 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 136 | }; | ||
| 137 | } | ||
| 113 | } | 138 | } |
| 114 | }; | 139 | }; |
| 115 | 140 | ||
| @@ -135,6 +160,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 135 | OutResponse::Accepted | 160 | OutResponse::Accepted |
| 136 | } | 161 | } |
| 137 | 162 | ||
| 163 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 164 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 165 | info!("The current HID protocol mode is: {}", protocol); | ||
| 166 | protocol | ||
| 167 | } | ||
| 168 | |||
| 169 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 170 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 171 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 172 | OutResponse::Accepted | ||
| 173 | } | ||
| 174 | |||
| 138 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 175 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | 176 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 140 | } | 177 | } |
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5628cb827..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | info!("Hello World!"); | 14 | info!("Hello World!"); |
| 15 | 15 | ||
| 16 | let mut delay = Delay; | 16 | let mut delay = Delay; |
| 17 | let mut adc = Adc::new(p.ADC1); | 17 | let mut adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 18 | let mut pin = p.PC1; | 18 | let mut pin = p.PC1; |
| 19 | 19 | ||
| 20 | let mut vrefint = adc.enable_vrefint(); | 20 | let mut vrefint = adc.enable_vrefint(); |
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 01b881c79..d61b1b2eb 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ use cortex_m::singleton; | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Peripherals; | 6 | use embassy_stm32::Peripherals; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime}; |
| 8 | use embassy_time::Instant; | 8 | use embassy_time::Instant; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| @@ -20,8 +20,8 @@ async fn adc_task(p: Peripherals) { | |||
| 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); |
| 22 | 22 | ||
| 23 | let adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new_with_config(p.ADC1, Default::default()); |
| 24 | let adc2 = Adc::new(p.ADC2); | 24 | let adc2 = Adc::new_with_config(p.ADC2, Default::default()); |
| 25 | 25 | ||
| 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( |
| 27 | p.DMA2_CH0, | 27 | p.DMA2_CH0, |
| @@ -31,6 +31,7 @@ async fn adc_task(p: Peripherals) { | |||
| 31 | (p.PA2.degrade_adc(), SampleTime::CYCLES112), | 31 | (p.PA2.degrade_adc(), SampleTime::CYCLES112), |
| 32 | ] | 32 | ] |
| 33 | .into_iter(), | 33 | .into_iter(), |
| 34 | RegularConversionMode::Continuous, | ||
| 34 | ); | 35 | ); |
| 35 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( | 36 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered( |
| 36 | p.DMA2_CH2, | 37 | p.DMA2_CH2, |
| @@ -40,6 +41,7 @@ async fn adc_task(p: Peripherals) { | |||
| 40 | (p.PA3.degrade_adc(), SampleTime::CYCLES112), | 41 | (p.PA3.degrade_adc(), SampleTime::CYCLES112), |
| 41 | ] | 42 | ] |
| 42 | .into_iter(), | 43 | .into_iter(), |
| 44 | RegularConversionMode::Continuous, | ||
| 43 | ); | 45 | ); |
| 44 | 46 | ||
| 45 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around | 47 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around |
diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index a3afb887c..9971e43f5 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| @@ -11,7 +11,9 @@ use embassy_stm32::gpio::Pull; | |||
| 11 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::usb::Driver; | 12 | use embassy_stm32::usb::Driver; |
| 13 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 13 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 15 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 16 | use embassy_usb::{Builder, Handler}; | 18 | use embassy_usb::{Builder, Handler}; |
| 17 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 19 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| @@ -21,6 +23,8 @@ bind_interrupts!(struct Irqs { | |||
| 21 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 23 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |
| 22 | }); | 24 | }); |
| 23 | 25 | ||
| 26 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 27 | |||
| 24 | // If you are trying this and your USB device doesn't connect, the most | 28 | // If you are trying this and your USB device doesn't connect, the most |
| 25 | // common issues are the RCC config and vbus_detection | 29 | // common issues are the RCC config and vbus_detection |
| 26 | // | 30 | // |
| @@ -70,6 +74,10 @@ async fn main(_spawner: Spawner) { | |||
| 70 | config.serial_number = Some("12345678"); | 74 | config.serial_number = Some("12345678"); |
| 71 | config.max_power = 100; | 75 | config.max_power = 100; |
| 72 | config.max_packet_size_0 = 64; | 76 | config.max_packet_size_0 = 64; |
| 77 | config.composite_with_iads = false; | ||
| 78 | config.device_class = 0; | ||
| 79 | config.device_sub_class = 0; | ||
| 80 | config.device_protocol = 0; | ||
| 73 | 81 | ||
| 74 | // Create embassy-usb DeviceBuilder using the driver and config. | 82 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 75 | // It needs some buffers for building the descriptors. | 83 | // It needs some buffers for building the descriptors. |
| @@ -101,6 +109,8 @@ async fn main(_spawner: Spawner) { | |||
| 101 | request_handler: None, | 109 | request_handler: None, |
| 102 | poll_ms: 60, | 110 | poll_ms: 60, |
| 103 | max_packet_size: 8, | 111 | max_packet_size: 8, |
| 112 | hid_subclass: HidSubclass::Boot, | ||
| 113 | hid_boot_protocol: HidBootProtocol::Keyboard, | ||
| 104 | }; | 114 | }; |
| 105 | 115 | ||
| 106 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | 116 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); |
| @@ -121,32 +131,46 @@ async fn main(_spawner: Spawner) { | |||
| 121 | button.wait_for_rising_edge().await; | 131 | button.wait_for_rising_edge().await; |
| 122 | // signal_pin.wait_for_high().await; | 132 | // signal_pin.wait_for_high().await; |
| 123 | info!("Button pressed!"); | 133 | info!("Button pressed!"); |
| 124 | // Create a report with the A key pressed. (no shift modifier) | 134 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 125 | let report = KeyboardReport { | 135 | match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { |
| 126 | keycodes: [4, 0, 0, 0, 0, 0], | 136 | Ok(()) => {} |
| 127 | leds: 0, | 137 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 128 | modifier: 0, | 138 | }; |
| 129 | reserved: 0, | 139 | } else { |
| 130 | }; | 140 | // Create a report with the A key pressed. (no shift modifier) |
| 131 | // Send the report. | 141 | let report = KeyboardReport { |
| 132 | match writer.write_serialize(&report).await { | 142 | keycodes: [4, 0, 0, 0, 0, 0], |
| 133 | Ok(()) => {} | 143 | leds: 0, |
| 134 | Err(e) => warn!("Failed to send report: {:?}", e), | 144 | modifier: 0, |
| 135 | }; | 145 | reserved: 0, |
| 146 | }; | ||
| 147 | // Send the report. | ||
| 148 | match writer.write_serialize(&report).await { | ||
| 149 | Ok(()) => {} | ||
| 150 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 151 | }; | ||
| 152 | } | ||
| 136 | 153 | ||
| 137 | button.wait_for_falling_edge().await; | 154 | button.wait_for_falling_edge().await; |
| 138 | // signal_pin.wait_for_low().await; | 155 | // signal_pin.wait_for_low().await; |
| 139 | info!("Button released!"); | 156 | info!("Button released!"); |
| 140 | let report = KeyboardReport { | 157 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 141 | keycodes: [0, 0, 0, 0, 0, 0], | 158 | match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { |
| 142 | leds: 0, | 159 | Ok(()) => {} |
| 143 | modifier: 0, | 160 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 144 | reserved: 0, | 161 | }; |
| 145 | }; | 162 | } else { |
| 146 | match writer.write_serialize(&report).await { | 163 | let report = KeyboardReport { |
| 147 | Ok(()) => {} | 164 | keycodes: [0, 0, 0, 0, 0, 0], |
| 148 | Err(e) => warn!("Failed to send report: {:?}", e), | 165 | leds: 0, |
| 149 | }; | 166 | modifier: 0, |
| 167 | reserved: 0, | ||
| 168 | }; | ||
| 169 | match writer.write_serialize(&report).await { | ||
| 170 | Ok(()) => {} | ||
| 171 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 172 | }; | ||
| 173 | } | ||
| 150 | } | 174 | } |
| 151 | }; | 175 | }; |
| 152 | 176 | ||
| @@ -172,6 +196,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 172 | OutResponse::Accepted | 196 | OutResponse::Accepted |
| 173 | } | 197 | } |
| 174 | 198 | ||
| 199 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 200 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 201 | info!("The current HID protocol mode is: {}", protocol); | ||
| 202 | protocol | ||
| 203 | } | ||
| 204 | |||
| 205 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 206 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 207 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 208 | OutResponse::Accepted | ||
| 209 | } | ||
| 210 | |||
| 175 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 211 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 176 | info!("Set idle rate for {:?} to {:?}", id, dur); | 212 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 177 | } | 213 | } |
diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 162a035f2..e83d01f88 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -9,7 +11,9 @@ use embassy_stm32::usb::Driver; | |||
| 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 11 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 10 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 11 | use embassy_usb::Builder; | 13 | use embassy_usb::Builder; |
| 12 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 14 | use embassy_usb::class::hid::{ |
| 15 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 16 | }; | ||
| 13 | use embassy_usb::control::OutResponse; | 17 | use embassy_usb::control::OutResponse; |
| 14 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 18 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -18,6 +22,8 @@ bind_interrupts!(struct Irqs { | |||
| 18 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 22 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |
| 19 | }); | 23 | }); |
| 20 | 24 | ||
| 25 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 26 | |||
| 21 | // If you are trying this and your USB device doesn't connect, the most | 27 | // If you are trying this and your USB device doesn't connect, the most |
| 22 | // common issues are the RCC config and vbus_detection | 28 | // common issues are the RCC config and vbus_detection |
| 23 | // | 29 | // |
| @@ -65,6 +71,10 @@ async fn main(_spawner: Spawner) { | |||
| 65 | config.manufacturer = Some("Embassy"); | 71 | config.manufacturer = Some("Embassy"); |
| 66 | config.product = Some("HID mouse example"); | 72 | config.product = Some("HID mouse example"); |
| 67 | config.serial_number = Some("12345678"); | 73 | config.serial_number = Some("12345678"); |
| 74 | config.composite_with_iads = false; | ||
| 75 | config.device_class = 0; | ||
| 76 | config.device_sub_class = 0; | ||
| 77 | config.device_protocol = 0; | ||
| 68 | 78 | ||
| 69 | // Create embassy-usb DeviceBuilder using the driver and config. | 79 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 70 | // It needs some buffers for building the descriptors. | 80 | // It needs some buffers for building the descriptors. |
| @@ -91,6 +101,8 @@ async fn main(_spawner: Spawner) { | |||
| 91 | request_handler: Some(&mut request_handler), | 101 | request_handler: Some(&mut request_handler), |
| 92 | poll_ms: 60, | 102 | poll_ms: 60, |
| 93 | max_packet_size: 8, | 103 | max_packet_size: 8, |
| 104 | hid_subclass: HidSubclass::Boot, | ||
| 105 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 94 | }; | 106 | }; |
| 95 | 107 | ||
| 96 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 108 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -108,16 +120,26 @@ async fn main(_spawner: Spawner) { | |||
| 108 | Timer::after_millis(500).await; | 120 | Timer::after_millis(500).await; |
| 109 | 121 | ||
| 110 | y = -y; | 122 | y = -y; |
| 111 | let report = MouseReport { | 123 | |
| 112 | buttons: 0, | 124 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 113 | x: 0, | 125 | let buttons = 0u8; |
| 114 | y, | 126 | let x = 0i8; |
| 115 | wheel: 0, | 127 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 116 | pan: 0, | 128 | Ok(()) => {} |
| 117 | }; | 129 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 118 | match writer.write_serialize(&report).await { | 130 | } |
| 119 | Ok(()) => {} | 131 | } else { |
| 120 | Err(e) => warn!("Failed to send report: {:?}", e), | 132 | let report = MouseReport { |
| 133 | buttons: 0, | ||
| 134 | x: 0, | ||
| 135 | y, | ||
| 136 | wheel: 0, | ||
| 137 | pan: 0, | ||
| 138 | }; | ||
| 139 | match writer.write_serialize(&report).await { | ||
| 140 | Ok(()) => {} | ||
| 141 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 142 | } | ||
| 121 | } | 143 | } |
| 122 | } | 144 | } |
| 123 | }; | 145 | }; |
| @@ -140,6 +162,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 140 | OutResponse::Accepted | 162 | OutResponse::Accepted |
| 141 | } | 163 | } |
| 142 | 164 | ||
| 165 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 166 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 167 | info!("The current HID protocol mode is: {}", protocol); | ||
| 168 | protocol | ||
| 169 | } | ||
| 170 | |||
| 171 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 172 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 173 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 174 | OutResponse::Accepted | ||
| 175 | } | ||
| 176 | |||
| 143 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 177 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 144 | info!("Set idle rate for {:?} to {:?}", id, dur); | 178 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 145 | } | 179 | } |
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index f6979889d..aa8b1771b 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; | 10 | use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -16,12 +16,14 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Adc oversample test"); | 17 | info!("Adc oversample test"); |
| 18 | 18 | ||
| 19 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); | 19 | let mut config = AdcConfig::default(); |
| 20 | let mut pin = p.PA1; | 20 | config.clock = Some(Clock::Async { div: Presc::DIV1 }); |
| 21 | config.oversampling_ratio = Some(Ovsr::MUL16); | ||
| 22 | config.oversampling_shift = Some(Ovss::NO_SHIFT); | ||
| 23 | config.oversampling_enable = Some(true); | ||
| 21 | 24 | ||
| 22 | adc.set_oversampling_ratio(Ovsr::MUL16); | 25 | let mut adc = Adc::new_with_config(p.ADC1, config); |
| 23 | adc.set_oversampling_shift(Ovss::NO_SHIFT); | 26 | let mut pin = p.PA1; |
| 24 | adc.oversampling_enable(true); | ||
| 25 | 27 | ||
| 26 | loop { | 28 | loop { |
| 27 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); | 29 | let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); |
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 695f37115..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -28,11 +28,16 @@ async fn main(_spawner: Spawner) { | |||
| 28 | let mut p = embassy_stm32::init(config); | 28 | let mut p = embassy_stm32::init(config); |
| 29 | info!("Hello World!"); | 29 | info!("Hello World!"); |
| 30 | 30 | ||
| 31 | let mut adc = Adc::new(p.ADC2); | 31 | let mut adc = Adc::new(p.ADC2, Default::default()); |
| 32 | |||
| 33 | let mut adc_temp = Adc::new(p.ADC1, Default::default()); | ||
| 34 | let mut temperature = adc_temp.enable_temperature(); | ||
| 32 | 35 | ||
| 33 | loop { | 36 | loop { |
| 34 | let measured = adc.blocking_read(&mut p.PA7, SampleTime::CYCLES24_5); | 37 | let measured = adc.blocking_read(&mut p.PA7, SampleTime::CYCLES24_5); |
| 38 | let temperature = adc_temp.blocking_read(&mut temperature, SampleTime::CYCLES24_5); | ||
| 35 | info!("measured: {}", measured); | 39 | info!("measured: {}", measured); |
| 40 | info!("temperature: {}", temperature); | ||
| 36 | Timer::after_millis(500).await; | 41 | Timer::after_millis(500).await; |
| 37 | } | 42 | } |
| 38 | } | 43 | } |
diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index a6e2f7d33..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs | |||
| @@ -30,16 +30,16 @@ async fn main(_spawner: Spawner) { | |||
| 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; | 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; |
| 31 | config.rcc.sys = Sysclk::PLL1_R; | 31 | config.rcc.sys = Sysclk::PLL1_R; |
| 32 | } | 32 | } |
| 33 | let mut p = embassy_stm32::init(config); | 33 | let p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 36 | adc.set_differential(&mut p.PA0, true); //p:pa0,n:pa1 | 36 | let mut differential_channel = (p.PA0, p.PA1); |
| 37 | 37 | ||
| 38 | // can also use | 38 | // can also use |
| 39 | // adc.set_differential_channel(1, true); | 39 | // adc.set_differential_channel(1, true); |
| 40 | info!("adc initialized"); | 40 | info!("adc initialized"); |
| 41 | loop { | 41 | loop { |
| 42 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES247_5); | 42 | let measured = adc.blocking_read(&mut differential_channel, SampleTime::CYCLES247_5); |
| 43 | info!("data: {}", measured); | 43 | info!("data: {}", measured); |
| 44 | Timer::after_millis(500).await; | 44 | Timer::after_millis(500).await; |
| 45 | } | 45 | } |
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index ef8b0c3c2..478b6b2ca 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | 33 | ||
| 34 | info!("Hello World!"); | 34 | info!("Hello World!"); |
| 35 | 35 | ||
| 36 | let mut adc = Adc::new(p.ADC1); | 36 | let mut adc = Adc::new(p.ADC1, Default::default()); |
| 37 | 37 | ||
| 38 | let mut dma = p.DMA1_CH1; | 38 | let mut dma = p.DMA1_CH1; |
| 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index 3ae2ff064..1e97fa925 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs | |||
| @@ -77,7 +77,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 77 | pwm.set_mms2(Mms2::UPDATE); | 77 | pwm.set_mms2(Mms2::UPDATE); |
| 78 | 78 | ||
| 79 | // Configure regular conversions with DMA | 79 | // Configure regular conversions with DMA |
| 80 | let adc1 = Adc::new(p.ADC1); | 80 | let adc1 = Adc::new(p.ADC1, Default::default()); |
| 81 | 81 | ||
| 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); | 82 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); |
| 83 | let pa0 = p.PC1.degrade_adc(); | 83 | let pa0 = p.PC1.degrade_adc(); |
diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index cb99ab2a7..87ffea4be 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs | |||
| @@ -9,7 +9,7 @@ use defmt::*; | |||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::Config; | 10 | use embassy_stm32::Config; |
| 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; | 11 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; |
| 12 | use embassy_stm32::adc::{Adc, SampleTime}; | 12 | use embassy_stm32::adc::{Adc, AdcConfig, SampleTime}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 15 | ||
| @@ -32,7 +32,8 @@ async fn main(_spawner: Spawner) { | |||
| 32 | } | 32 | } |
| 33 | let mut p = embassy_stm32::init(config); | 33 | let mut p = embassy_stm32::init(config); |
| 34 | 34 | ||
| 35 | let mut adc = Adc::new(p.ADC1); | 35 | let mut config = AdcConfig::default(); |
| 36 | |||
| 36 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | 37 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf |
| 37 | // page652 Oversampler | 38 | // page652 Oversampler |
| 38 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation | 39 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation |
| @@ -44,9 +45,11 @@ async fn main(_spawner: Spawner) { | |||
| 44 | // 0x05 oversampling ratio X64 | 45 | // 0x05 oversampling ratio X64 |
| 45 | // 0x06 oversampling ratio X128 | 46 | // 0x06 oversampling ratio X128 |
| 46 | // 0x07 oversampling ratio X256 | 47 | // 0x07 oversampling ratio X256 |
| 47 | adc.set_oversampling_ratio(0x03); // ratio X3 | 48 | config.oversampling_ratio = Some(0x03); // ratio X3 |
| 48 | adc.set_oversampling_shift(0b0000); // no shift | 49 | config.oversampling_shift = Some(0b0000); // no shift |
| 49 | adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); | 50 | config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true)); |
| 51 | |||
| 52 | let mut adc = Adc::new(p.ADC1, config); | ||
| 50 | 53 | ||
| 51 | loop { | 54 | loop { |
| 52 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); | 55 | let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); |
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 835bf5411..42766a5e3 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 8 | ||
| 9 | #[cortex_m_rt::entry] | 9 | #[cortex_m_rt::entry] |
| @@ -17,9 +17,12 @@ fn main() -> ! { | |||
| 17 | } | 17 | } |
| 18 | let p = embassy_stm32::init(config); | 18 | let p = embassy_stm32::init(config); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new(p.ADC1); | 20 | let mut config = AdcConfig::default(); |
| 21 | config.resolution = Some(Resolution::BITS8); | ||
| 22 | |||
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 21 | //adc.enable_vref(); | 24 | //adc.enable_vref(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 25 | |
| 23 | let mut channel = p.PC0; | 26 | let mut channel = p.PC0; |
| 24 | 27 | ||
| 25 | loop { | 28 | loop { |
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index ab1e9d2e9..550da95a4 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Config; | 6 | use embassy_stm32::Config; |
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | const DMA_BUF_LEN: usize = 512; | 10 | const DMA_BUF_LEN: usize = 512; |
| @@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) { | |||
| 20 | } | 20 | } |
| 21 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 22 | 22 | ||
| 23 | let mut adc = Adc::new(p.ADC1); | 23 | let adc = Adc::new(p.ADC1); |
| 24 | let adc_pin0 = p.PA0.degrade_adc(); | 24 | let adc_pin0 = p.PA0.degrade_adc(); |
| 25 | let adc_pin1 = p.PA1.degrade_adc(); | 25 | let adc_pin1 = p.PA1.degrade_adc(); |
| 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; | 26 | let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; |
| @@ -29,6 +29,7 @@ async fn main(_spawner: Spawner) { | |||
| 29 | p.DMA1_CH1, | 29 | p.DMA1_CH1, |
| 30 | &mut adc_dma_buf, | 30 | &mut adc_dma_buf, |
| 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), | 31 | [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), |
| 32 | RegularConversionMode::Continuous, | ||
| 32 | ); | 33 | ); |
| 33 | 34 | ||
| 34 | info!("starting measurement loop"); | 35 | info!("starting measurement loop"); |
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index b721f5b2e..d8f2de941 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| @@ -8,7 +10,9 @@ use embassy_stm32::usb::Driver; | |||
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; | 10 | use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; |
| 9 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 10 | use embassy_usb::Builder; | 12 | use embassy_usb::Builder; |
| 11 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | 13 | use embassy_usb::class::hid::{ |
| 14 | HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, | ||
| 15 | }; | ||
| 12 | use embassy_usb::control::OutResponse; | 16 | use embassy_usb::control::OutResponse; |
| 13 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | 17 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -17,6 +21,8 @@ bind_interrupts!(struct Irqs { | |||
| 17 | USB_FS => usb::InterruptHandler<peripherals::USB>; | 21 | USB_FS => usb::InterruptHandler<peripherals::USB>; |
| 18 | }); | 22 | }); |
| 19 | 23 | ||
| 24 | static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); | ||
| 25 | |||
| 20 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 22 | let mut config = Config::default(); | 28 | let mut config = Config::default(); |
| @@ -48,6 +54,10 @@ async fn main(_spawner: Spawner) { | |||
| 48 | config.serial_number = Some("12345678"); | 54 | config.serial_number = Some("12345678"); |
| 49 | config.max_power = 100; | 55 | config.max_power = 100; |
| 50 | config.max_packet_size_0 = 64; | 56 | config.max_packet_size_0 = 64; |
| 57 | config.composite_with_iads = false; | ||
| 58 | config.device_class = 0; | ||
| 59 | config.device_sub_class = 0; | ||
| 60 | config.device_protocol = 0; | ||
| 51 | 61 | ||
| 52 | // Create embassy-usb DeviceBuilder using the driver and config. | 62 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 53 | // It needs some buffers for building the descriptors. | 63 | // It needs some buffers for building the descriptors. |
| @@ -73,6 +83,8 @@ async fn main(_spawner: Spawner) { | |||
| 73 | request_handler: Some(&mut request_handler), | 83 | request_handler: Some(&mut request_handler), |
| 74 | poll_ms: 60, | 84 | poll_ms: 60, |
| 75 | max_packet_size: 8, | 85 | max_packet_size: 8, |
| 86 | hid_subclass: HidSubclass::Boot, | ||
| 87 | hid_boot_protocol: HidBootProtocol::Mouse, | ||
| 76 | }; | 88 | }; |
| 77 | 89 | ||
| 78 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | 90 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); |
| @@ -90,16 +102,26 @@ async fn main(_spawner: Spawner) { | |||
| 90 | Timer::after_millis(500).await; | 102 | Timer::after_millis(500).await; |
| 91 | 103 | ||
| 92 | y = -y; | 104 | y = -y; |
| 93 | let report = MouseReport { | 105 | |
| 94 | buttons: 0, | 106 | if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { |
| 95 | x: 0, | 107 | let buttons = 0u8; |
| 96 | y, | 108 | let x = 0i8; |
| 97 | wheel: 0, | 109 | match writer.write(&[buttons, x as u8, y as u8]).await { |
| 98 | pan: 0, | 110 | Ok(()) => {} |
| 99 | }; | 111 | Err(e) => warn!("Failed to send boot report: {:?}", e), |
| 100 | match writer.write_serialize(&report).await { | 112 | } |
| 101 | Ok(()) => {} | 113 | } else { |
| 102 | Err(e) => warn!("Failed to send report: {:?}", e), | 114 | let report = MouseReport { |
| 115 | buttons: 0, | ||
| 116 | x: 0, | ||
| 117 | y, | ||
| 118 | wheel: 0, | ||
| 119 | pan: 0, | ||
| 120 | }; | ||
| 121 | match writer.write_serialize(&report).await { | ||
| 122 | Ok(()) => {} | ||
| 123 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 124 | } | ||
| 103 | } | 125 | } |
| 104 | } | 126 | } |
| 105 | }; | 127 | }; |
| @@ -122,6 +144,18 @@ impl RequestHandler for MyRequestHandler { | |||
| 122 | OutResponse::Accepted | 144 | OutResponse::Accepted |
| 123 | } | 145 | } |
| 124 | 146 | ||
| 147 | fn get_protocol(&self) -> HidProtocolMode { | ||
| 148 | let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); | ||
| 149 | info!("The current HID protocol mode is: {}", protocol); | ||
| 150 | protocol | ||
| 151 | } | ||
| 152 | |||
| 153 | fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { | ||
| 154 | info!("Switching to HID protocol mode: {}", protocol); | ||
| 155 | HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); | ||
| 156 | OutResponse::Accepted | ||
| 157 | } | ||
| 158 | |||
| 125 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | 159 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { |
| 126 | info!("Set idle rate for {:?} to {:?}", id, dur); | 160 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 127 | } | 161 | } |
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4fbc6f17f..53bd37303 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::Config; | 5 | use embassy_stm32::Config; |
| 6 | use embassy_stm32::adc::{Adc, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; |
| 7 | use embassy_time::Duration; | 7 | use embassy_time::Duration; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -18,8 +18,9 @@ fn main() -> ! { | |||
| 18 | } | 18 | } |
| 19 | let p = embassy_stm32::init(config); | 19 | let p = embassy_stm32::init(config); |
| 20 | 20 | ||
| 21 | let mut adc = Adc::new(p.ADC1); | 21 | let mut config = AdcConfig::default(); |
| 22 | adc.set_resolution(Resolution::BITS8); | 22 | config.resolution = Some(Resolution::BITS8); |
| 23 | let mut adc = Adc::new_with_config(p.ADC1, config); | ||
| 23 | let mut channel = p.PC0; | 24 | let mut channel = p.PC0; |
| 24 | 25 | ||
| 25 | loop { | 26 | loop { |
diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 99944f7c7..6b9a91d6e 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::adc::{self, AdcChannel, SampleTime, adc4}; | 5 | use embassy_stm32::adc::{self, AdcChannel, AdcConfig, SampleTime, adc4}; |
| 6 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 7 | 7 | ||
| 8 | #[embassy_executor::main] | 8 | #[embassy_executor::main] |
| @@ -12,19 +12,21 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 12 | let mut p = embassy_stm32::init(config); | 12 | let mut p = embassy_stm32::init(config); |
| 13 | 13 | ||
| 14 | // **** ADC1 init **** | 14 | // **** ADC1 init **** |
| 15 | let mut adc1 = adc::Adc::new(p.ADC1); | 15 | let mut config = AdcConfig::default(); |
| 16 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 17 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 18 | let mut adc1 = adc::Adc::new_with_config(p.ADC1, config); | ||
| 16 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 | 19 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 |
| 17 | let mut adc1_pin2 = p.PA2; // A1 | 20 | let mut adc1_pin2 = p.PA2; // A1 |
| 18 | adc1.set_resolution(adc::Resolution::BITS14); | ||
| 19 | adc1.set_averaging(adc::Averaging::Samples1024); | ||
| 20 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 21 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 21 | 22 | ||
| 22 | // **** ADC2 init **** | 23 | // **** ADC2 init **** |
| 23 | let mut adc2 = adc::Adc::new(p.ADC2); | 24 | let mut config = AdcConfig::default(); |
| 25 | config.averaging = Some(adc::Averaging::Samples1024); | ||
| 26 | config.resolution = Some(adc::Resolution::BITS14); | ||
| 27 | let mut adc2 = adc::Adc::new_with_config(p.ADC2, config); | ||
| 24 | let mut adc2_pin1 = p.PC3; // A2 | 28 | let mut adc2_pin1 = p.PC3; // A2 |
| 25 | let mut adc2_pin2 = p.PB0; // A3 | 29 | let mut adc2_pin2 = p.PB0; // A3 |
| 26 | adc2.set_resolution(adc::Resolution::BITS14); | ||
| 27 | adc2.set_averaging(adc::Averaging::Samples1024); | ||
| 28 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); | 30 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); |
| 29 | 31 | ||
| 30 | // **** ADC4 init **** | 32 | // **** ADC4 init **** |
