diff options
| author | Alexandros Liarokapis <[email protected]> | 2024-06-21 23:37:58 +0300 |
|---|---|---|
| committer | Andres Vahter <[email protected]> | 2024-07-02 17:07:18 +0300 |
| commit | 02b096915fbf138602e2ad8c6e1d85d531882daf (patch) | |
| tree | 2b0ab5246b5dc1032f8ba41e25a8578d6ce62da5 | |
| parent | 9bdb697cd96d36dd6e42e8680e3f2f7983e35f74 (diff) | |
add asynchrous sequence read support to adc v4
| -rw-r--r-- | embassy-stm32/build.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v4.rs | 137 |
2 files changed, 126 insertions, 12 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 24e2226a2..df866b5ff 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1182,6 +1182,7 @@ fn main() { | |||
| 1182 | (("adc", "ADC1"), quote!(crate::adc::RxDma)), | 1182 | (("adc", "ADC1"), quote!(crate::adc::RxDma)), |
| 1183 | (("adc", "ADC2"), quote!(crate::adc::RxDma)), | 1183 | (("adc", "ADC2"), quote!(crate::adc::RxDma)), |
| 1184 | (("adc", "ADC3"), quote!(crate::adc::RxDma)), | 1184 | (("adc", "ADC3"), quote!(crate::adc::RxDma)), |
| 1185 | (("adc", "ADC4"), quote!(crate::adc::RxDma)), | ||
| 1185 | (("ucpd", "RX"), quote!(crate::ucpd::RxDma)), | 1186 | (("ucpd", "RX"), quote!(crate::ucpd::RxDma)), |
| 1186 | (("ucpd", "TX"), quote!(crate::ucpd::TxDma)), | 1187 | (("ucpd", "TX"), quote!(crate::ucpd::TxDma)), |
| 1187 | (("usart", "RX"), quote!(crate::usart::RxDma)), | 1188 | (("usart", "RX"), quote!(crate::usart::RxDma)), |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 50db646fe..f4b62d80e 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -1,8 +1,12 @@ | |||
| 1 | #[allow(unused)] | 1 | #[allow(unused)] |
| 2 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; | 2 | use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; |
| 3 | use pac::adc::vals::{Adstp, Dmngt}; | ||
| 3 | use pac::adccommon::vals::Presc; | 4 | use pac::adccommon::vals::Presc; |
| 4 | 5 | ||
| 5 | use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; | 6 | use super::{ |
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | ||
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 6 | use crate::time::Hertz; | 10 | use crate::time::Hertz; |
| 7 | use crate::{pac, rcc, Peripheral}; | 11 | use crate::{pac, rcc, Peripheral}; |
| 8 | 12 | ||
| @@ -34,7 +38,7 @@ const VBAT_CHANNEL: u8 = 17; | |||
| 34 | /// Internal voltage reference channel. | 38 | /// Internal voltage reference channel. |
| 35 | pub struct VrefInt; | 39 | pub struct VrefInt; |
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 40 | impl<T: Instance> AdcChannel<T> for VrefInt {} |
| 37 | impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { | 41 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { |
| 38 | fn channel(&self) -> u8 { | 42 | fn channel(&self) -> u8 { |
| 39 | VREF_CHANNEL | 43 | VREF_CHANNEL |
| 40 | } | 44 | } |
| @@ -43,7 +47,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { | |||
| 43 | /// Internal temperature channel. | 47 | /// Internal temperature channel. |
| 44 | pub struct Temperature; | 48 | pub struct Temperature; |
| 45 | impl<T: Instance> AdcChannel<T> for Temperature {} | 49 | impl<T: Instance> AdcChannel<T> for Temperature {} |
| 46 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | 50 | impl<T: Instance> SealedAdcChannel<T> for Temperature { |
| 47 | fn channel(&self) -> u8 { | 51 | fn channel(&self) -> u8 { |
| 48 | TEMP_CHANNEL | 52 | TEMP_CHANNEL |
| 49 | } | 53 | } |
| @@ -52,7 +56,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | |||
| 52 | /// Internal battery voltage channel. | 56 | /// Internal battery voltage channel. |
| 53 | pub struct Vbat; | 57 | pub struct Vbat; |
| 54 | impl<T: Instance> AdcChannel<T> for Vbat {} | 58 | impl<T: Instance> AdcChannel<T> for Vbat {} |
| 55 | impl<T: Instance> super::SealedAdcChannel<T> for Vbat { | 59 | impl<T: Instance> SealedAdcChannel<T> for Vbat { |
| 56 | fn channel(&self) -> u8 { | 60 | fn channel(&self) -> u8 { |
| 57 | VBAT_CHANNEL | 61 | VBAT_CHANNEL |
| 58 | } | 62 | } |
| @@ -247,6 +251,11 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 247 | self.sample_time = sample_time; | 251 | self.sample_time = sample_time; |
| 248 | } | 252 | } |
| 249 | 253 | ||
| 254 | /// Get the ADC sample time. | ||
| 255 | pub fn sample_time(&self) -> SampleTime { | ||
| 256 | self.sample_time | ||
| 257 | } | ||
| 258 | |||
| 250 | /// Set the ADC resolution. | 259 | /// Set the ADC resolution. |
| 251 | pub fn set_resolution(&mut self, resolution: Resolution) { | 260 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 252 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 261 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| @@ -273,25 +282,120 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 273 | 282 | ||
| 274 | /// Read an ADC channel. | 283 | /// Read an ADC channel. |
| 275 | pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 284 | pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { |
| 276 | channel.setup(); | 285 | self.read_channel(channel) |
| 286 | } | ||
| 287 | |||
| 288 | /// Asynchronously read from sequence of ADC channels. | ||
| 289 | pub async fn read_async( | ||
| 290 | &mut self, | ||
| 291 | rx_dma: &mut impl RxDma<T>, | ||
| 292 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 293 | data: &mut [u16], | ||
| 294 | ) { | ||
| 295 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 296 | |||
| 297 | assert!( | ||
| 298 | sequence.len() <= 16, | ||
| 299 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 300 | ); | ||
| 301 | |||
| 302 | // Ensure no conversions are ongoing | ||
| 303 | Self::cancel_conversions(); | ||
| 304 | |||
| 305 | // Set sequence length | ||
| 306 | T::regs().sqr1().modify(|w| { | ||
| 307 | w.set_l(sequence.len() as u8 - 1); | ||
| 308 | }); | ||
| 309 | |||
| 310 | // Configure channels and ranks | ||
| 311 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 312 | Self::configure_channel(channel, sample_time); | ||
| 313 | match i { | ||
| 314 | 0..=3 => { | ||
| 315 | T::regs().sqr1().modify(|w| { | ||
| 316 | w.set_sq(i, channel.channel()); | ||
| 317 | }); | ||
| 318 | } | ||
| 319 | 4..=8 => { | ||
| 320 | T::regs().sqr2().modify(|w| { | ||
| 321 | w.set_sq(i - 4, channel.channel()); | ||
| 322 | }); | ||
| 323 | } | ||
| 324 | 9..=13 => { | ||
| 325 | T::regs().sqr3().modify(|w| { | ||
| 326 | w.set_sq(i - 9, channel.channel()); | ||
| 327 | }); | ||
| 328 | } | ||
| 329 | 14..=15 => { | ||
| 330 | T::regs().sqr4().modify(|w| { | ||
| 331 | w.set_sq(i - 14, channel.channel()); | ||
| 332 | }); | ||
| 333 | } | ||
| 334 | _ => unreachable!(), | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | // Set continuous mode with oneshot dma. | ||
| 339 | // Clear overrun flag before starting transfer. | ||
| 340 | |||
| 341 | T::regs().isr().modify(|reg| { | ||
| 342 | reg.set_ovr(true); | ||
| 343 | }); | ||
| 344 | T::regs().cfgr().modify(|reg| { | ||
| 345 | reg.set_cont(true); | ||
| 346 | reg.set_dmngt(Dmngt::DMA_ONESHOT); | ||
| 347 | }); | ||
| 348 | |||
| 349 | let request = rx_dma.request(); | ||
| 350 | let transfer = unsafe { | ||
| 351 | Transfer::new_read( | ||
| 352 | rx_dma, | ||
| 353 | request, | ||
| 354 | T::regs().dr().as_ptr() as *mut u16, | ||
| 355 | data, | ||
| 356 | Default::default(), | ||
| 357 | ) | ||
| 358 | }; | ||
| 277 | 359 | ||
| 278 | self.read_channel(channel.channel()) | 360 | // Start conversion |
| 361 | T::regs().cr().modify(|reg| { | ||
| 362 | reg.set_adstart(true); | ||
| 363 | }); | ||
| 364 | |||
| 365 | // Wait for conversion sequence to finish. | ||
| 366 | transfer.await; | ||
| 367 | |||
| 368 | // Ensure conversions are finished. | ||
| 369 | Self::cancel_conversions(); | ||
| 370 | |||
| 371 | // Reset configuration. | ||
| 372 | T::regs().cfgr().modify(|reg| { | ||
| 373 | reg.set_cont(false); | ||
| 374 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 375 | }); | ||
| 279 | } | 376 | } |
| 280 | 377 | ||
| 281 | fn read_channel(&mut self, channel: u8) -> u16 { | 378 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 282 | // Configure channel | 379 | channel.setup(); |
| 283 | Self::set_channel_sample_time(channel, self.sample_time); | 380 | |
| 381 | let channel = channel.channel(); | ||
| 382 | |||
| 383 | Self::set_channel_sample_time(channel, sample_time); | ||
| 284 | 384 | ||
| 285 | #[cfg(stm32h7)] | 385 | #[cfg(stm32h7)] |
| 286 | { | 386 | { |
| 287 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | 387 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); |
| 288 | T::regs() | 388 | T::regs() |
| 289 | .pcsel() | 389 | .pcsel() |
| 290 | .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | 390 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); |
| 291 | } | 391 | } |
| 392 | } | ||
| 292 | 393 | ||
| 293 | T::regs().sqr1().write(|reg| { | 394 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { |
| 294 | reg.set_sq(0, channel); | 395 | Self::configure_channel(channel, self.sample_time); |
| 396 | |||
| 397 | T::regs().sqr1().modify(|reg| { | ||
| 398 | reg.set_sq(0, channel.channel()); | ||
| 295 | reg.set_l(0); | 399 | reg.set_l(0); |
| 296 | }); | 400 | }); |
| 297 | 401 | ||
| @@ -306,4 +410,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 306 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 410 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); |
| 307 | } | 411 | } |
| 308 | } | 412 | } |
| 413 | |||
| 414 | fn cancel_conversions() { | ||
| 415 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 416 | T::regs().cr().modify(|reg| { | ||
| 417 | reg.set_adstp(Adstp::STOP); | ||
| 418 | }); | ||
| 419 | while T::regs().cr().read().adstart() {} | ||
| 420 | } | ||
| 421 | } | ||
| 309 | } | 422 | } |
