1use core::marker::PhantomData;
4use core::ops::Deref;
5use embedded_hal_02::adc::{Channel, OneShot};
6use fugit::HertzU32 as Hertz;
7
8#[cfg(all(feature = "stm32f103", any(feature = "high", feature = "xl")))]
9use crate::dma::dma2;
10use crate::dma::{dma1, CircBuffer, Receive, RxDma, Transfer, TransferPayload, W};
11use crate::gpio::{self, Analog};
12use crate::rcc::{Enable, Rcc, Reset};
13use crate::time::kHz;
14use core::sync::atomic::{self, Ordering};
15use cortex_m::asm::delay;
16use embedded_dma::WriteBuffer;
17
18use crate::pac::{self, RCC};
19use crate::pacext::adc::{AdcRB, Cr1W, Cr2R, Cr2W, Dr, ExtSelW};
20
21const TEMP_CHANNEL: u8 = 16;
22const VREF_CHANNEL: u8 = 17;
23
24pub struct Continuous;
26pub struct Scan;
28
29pub struct Adc<ADC> {
31 rb: ADC,
32 sample_time: SampleTime,
33 align: Align,
34 sysclk: Hertz,
35 adcclk: Hertz,
36}
37
38#[allow(non_camel_case_types)]
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum SampleTime {
44 T_1,
46 T_7,
48 T_13,
50 T_28,
52 T_41,
54 T_55,
56 T_71,
58 T_239,
60}
61
62impl Default for SampleTime {
63 fn default() -> Self {
65 SampleTime::T_28
66 }
67}
68
69impl From<SampleTime> for pac::adc1::smpr1::SMP10 {
70 fn from(val: SampleTime) -> Self {
71 use SampleTime::*;
72 match val {
73 T_1 => Self::Cycles1_5,
74 T_7 => Self::Cycles7_5,
75 T_13 => Self::Cycles13_5,
76 T_28 => Self::Cycles28_5,
77 T_41 => Self::Cycles41_5,
78 T_55 => Self::Cycles55_5,
79 T_71 => Self::Cycles71_5,
80 T_239 => Self::Cycles239_5,
81 }
82 }
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub enum Align {
88 Right,
90 Left,
92}
93
94impl Default for Align {
95 fn default() -> Self {
97 Align::Right
98 }
99}
100
101impl From<Align> for pac::adc1::cr2::ALIGN {
102 fn from(val: Align) -> Self {
103 match val {
104 Align::Right => Self::Right,
105 Align::Left => Self::Left,
106 }
107 }
108}
109
110macro_rules! adc_pins {
111 ($ADC:ty, $($pin:ty => $chan:literal),+ $(,)*) => {
112 $(
113 impl Channel<$ADC> for $pin {
114 type ID = u8;
115
116 fn channel() -> u8 { $chan }
117 }
118 )+
119 };
120}
121
122adc_pins!(pac::ADC1,
123 gpio::PA0<Analog> => 0,
124 gpio::PA1<Analog> => 1,
125 gpio::PA2<Analog> => 2,
126 gpio::PA3<Analog> => 3,
127 gpio::PA4<Analog> => 4,
128 gpio::PA5<Analog> => 5,
129 gpio::PA6<Analog> => 6,
130 gpio::PA7<Analog> => 7,
131 gpio::PB0<Analog> => 8,
132 gpio::PB1<Analog> => 9,
133 gpio::PC0<Analog> => 10,
134 gpio::PC1<Analog> => 11,
135 gpio::PC2<Analog> => 12,
136 gpio::PC3<Analog> => 13,
137 gpio::PC4<Analog> => 14,
138 gpio::PC5<Analog> => 15,
139);
140
141#[cfg(any(feature = "stm32f103", feature = "connectivity"))]
142adc_pins!(pac::ADC2,
143 gpio::PA0<Analog> => 0,
144 gpio::PA1<Analog> => 1,
145 gpio::PA2<Analog> => 2,
146 gpio::PA3<Analog> => 3,
147 gpio::PA4<Analog> => 4,
148 gpio::PA5<Analog> => 5,
149 gpio::PA6<Analog> => 6,
150 gpio::PA7<Analog> => 7,
151 gpio::PB0<Analog> => 8,
152 gpio::PB1<Analog> => 9,
153 gpio::PC0<Analog> => 10,
154 gpio::PC1<Analog> => 11,
155 gpio::PC2<Analog> => 12,
156 gpio::PC3<Analog> => 13,
157 gpio::PC4<Analog> => 14,
158 gpio::PC5<Analog> => 15,
159);
160
161#[cfg(all(feature = "stm32f103", any(feature = "high", feature = "xl")))]
162adc_pins!(pac::ADC3,
163 gpio::PA0<Analog> => 0,
164 gpio::PA1<Analog> => 1,
165 gpio::PA2<Analog> => 2,
166 gpio::PA3<Analog> => 3,
167 gpio::PF6<Analog> => 4,
168 gpio::PF7<Analog> => 5,
169 gpio::PF8<Analog> => 6,
170 gpio::PF9<Analog> => 7,
171 gpio::PF10<Analog> => 8,
172 gpio::PC0<Analog> => 10,
173 gpio::PC1<Analog> => 11,
174 gpio::PC2<Analog> => 12,
175 gpio::PC3<Analog> => 13,
176);
177
178#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
180pub struct StoredConfig(SampleTime, Align);
181
182pub trait Instance:
183 crate::Sealed + crate::Ptr<RB: AdcRB> + Deref<Target = Self::RB> + Reset + Enable
184{
185 type ExtSel;
186 #[doc(hidden)]
187 fn set_extsel(&self, trigger: Self::ExtSel);
188}
189
190impl Instance for pac::ADC1 {
191 type ExtSel = crate::pac::adc1::cr2::EXTSEL;
192 fn set_extsel(&self, trigger: Self::ExtSel) {
193 self.cr2().modify(|_, w| w.extsel().variant(trigger));
194 }
195}
196#[cfg(any(feature = "stm32f103", feature = "connectivity"))]
197impl Instance for pac::ADC2 {
198 type ExtSel = crate::pac::adc2::cr2::EXTSEL;
199 fn set_extsel(&self, trigger: Self::ExtSel) {
200 self.cr2().modify(|_, w| w.extsel().variant(trigger));
201 }
202}
203#[cfg(all(feature = "stm32f103", any(feature = "high", feature = "xl")))]
204impl Instance for pac::ADC3 {
205 type ExtSel = crate::pac::adc3::cr2::EXTSEL;
206 fn set_extsel(&self, trigger: Self::ExtSel) {
207 self.cr2().modify(|_, w| w.extsel().variant(trigger));
208 }
209}
210
211pub trait AdcExt: Sized + Instance {
212 fn adc(self, rcc: &mut Rcc) -> Adc<Self>;
213}
214
215impl<ADC: Instance> AdcExt for ADC {
216 fn adc(self, rcc: &mut Rcc) -> Adc<Self> {
217 Adc::new(self, rcc)
218 }
219}
220
221impl<ADC: Instance> Adc<ADC> {
222 pub fn new(adc: ADC, rcc: &mut Rcc) -> Self {
227 let mut s = Self {
228 rb: adc,
229 sample_time: SampleTime::default(),
230 align: Align::default(),
231 sysclk: rcc.clocks.sysclk(),
232 adcclk: rcc.clocks.adcclk(),
233 };
234 s.enable_clock(rcc);
235 s.power_down();
236 s.reset(rcc);
237 s.setup_oneshot();
238 s.power_up();
239
240 if s.adcclk < kHz(2500) {
244 let two_adc_cycles = s.sysclk / s.adcclk * 2;
245 let already_delayed = s.sysclk / kHz(800);
246 if two_adc_cycles > already_delayed {
247 delay(two_adc_cycles - already_delayed);
248 }
249 }
250 s.calibrate();
251 s
252 }
253
254 pub fn save_cfg(&mut self) -> StoredConfig {
256 StoredConfig(self.sample_time, self.align)
257 }
258
259 pub fn restore_cfg(&mut self, cfg: StoredConfig) {
261 self.sample_time = cfg.0;
262 self.align = cfg.1;
263 }
264
265 pub fn default_cfg(&mut self) -> StoredConfig {
267 let cfg = self.save_cfg();
268 self.sample_time = SampleTime::default();
269 self.align = Align::default();
270 cfg
271 }
272
273 pub fn set_sample_time(&mut self, t_samp: SampleTime) {
277 self.sample_time = t_samp;
278 }
279
280 pub fn set_align(&mut self, align: Align) {
284 self.align = align;
285 }
286
287 pub fn max_sample(&self) -> u16 {
289 match self.align {
290 Align::Left => u16::MAX,
291 Align::Right => (1 << 12) - 1,
292 }
293 }
294
295 #[inline(always)]
296 pub fn set_external_trigger(&mut self, trigger: ADC::ExtSel) {
297 self.rb.set_extsel(trigger);
298 }
299
300 fn power_up(&mut self) {
301 self.rb.cr2().modify(|_, w| w.adon().set_bit());
302
303 delay(self.sysclk / kHz(800));
308 }
309
310 fn power_down(&mut self) {
311 self.rb.cr2().modify(|_, w| w.adon().clear_bit());
312 }
313
314 fn reset(&mut self, rcc: &mut RCC) {
315 ADC::reset(rcc);
316 }
317
318 fn enable_clock(&mut self, rcc: &mut RCC) {
319 ADC::enable(rcc);
320 }
321
322 fn disable_clock(&mut self, rcc: &mut RCC) {
323 ADC::disable(rcc);
324 }
325
326 fn calibrate(&mut self) {
327 self.rb.cr2().modify(|_, w| w.rstcal().set_bit());
329 while self.rb.cr2().read().rstcal().bit_is_set() {}
330
331 self.rb.cr2().modify(|_, w| w.cal().set_bit());
333 while self.rb.cr2().read().cal().bit_is_set() {}
334 }
335
336 fn setup_oneshot(&mut self) {
337 self.rb.cr2().modify(|_, w| {
338 w.cont().clear_bit();
339 w.exttrig().set_bit();
340 w.select_swstart()
341 });
342
343 self.rb
344 .cr1()
345 .modify(|_, w| w.scan().clear_bit().discen().set_bit());
346
347 self.rb.sqr1().modify(|_, w| w.l().set(0b0));
348 }
349
350 fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime) {
351 let sample_time = sample_time.into();
352 match chan {
353 0..=9 => self
354 .rb
355 .smpr2()
356 .modify(|_, w| w.smp(chan).variant(sample_time)),
357 10..=17 => self
358 .rb
359 .smpr1()
360 .modify(|_, w| w.smp(chan - 10).variant(sample_time)),
361 _ => unreachable!(),
362 };
363 }
364
365 fn set_regular_sequence(&mut self, channels: &[u8]) {
366 let mut iter = channels.chunks(6);
367 unsafe {
368 if let Some(chunk) = iter.next() {
369 self.rb.sqr3().write(|w| {
370 for (i, &c) in chunk.iter().enumerate() {
371 w.sq(i as u8).bits(c);
372 }
373 w
374 });
375 }
376 if let Some(chunk) = iter.next() {
377 self.rb.sqr2().write(|w| {
378 for (i, &c) in chunk.iter().enumerate() {
379 w.sq(i as u8).bits(c);
380 }
381 w
382 });
383 }
384 self.rb.sqr1().write(|w| {
385 if let Some(chunk) = iter.next() {
386 for (i, &c) in chunk.iter().enumerate() {
387 w.sq(i as u8).bits(c);
388 }
389 }
390 w.l().set((channels.len() - 1) as u8)
391 });
392 }
393 }
394
395 fn set_continuous_mode(&mut self, continuous: bool) {
396 self.rb.cr2().modify(|_, w| w.cont().bit(continuous));
397 }
398
399 fn set_discontinuous_mode(&mut self, channels_count: Option<u8>) {
400 self.rb.cr1().modify(|_, w| match channels_count {
401 Some(count) => w.discen().set_bit().discnum().set(count),
402 None => w.discen().clear_bit(),
403 });
404 }
405
406 fn convert(&mut self, chan: u8) -> u16 {
420 self.rb.dr().read().data().bits();
424
425 self.set_channel_sample_time(chan, self.sample_time);
426 self.rb.sqr3().modify(|_, w| unsafe { w.sq1().bits(chan) });
427
428 self.rb.cr2().modify(|_, w| {
430 w.swstart().set_bit();
431 w.align().variant(self.align.into())
432 });
433 while self.rb.cr2().read().swstart().bit_is_set() {}
434 while self.rb.sr().read().eoc().bit_is_clear() {}
436
437 let res = self.rb.dr().read().data().bits();
438 res
439 }
440
441 pub fn release(mut self, rcc: &mut RCC) -> ADC {
443 self.power_down();
444 self.disable_clock(rcc);
445 self.rb
446 }
447
448 pub fn enable_eoc_interrupt(&mut self) {
450 self.rb.cr1().write(|w| w.eocie().set_bit());
451 }
452
453 pub fn disable_eoc_interrupt(&mut self) {
455 self.rb.cr1().write(|w| w.eocie().clear_bit());
456 }
457
458 pub fn enable_jeoc_interrupt(&mut self) {
460 self.rb.cr1().write(|w| w.jeocie().set_bit());
461 }
462
463 pub fn disable_jeoc_interrupt(&mut self) {
465 self.rb.cr1().write(|w| w.jeocie().clear_bit());
466 }
467}
468
469impl<ADC: Instance> ChannelTimeSequence for Adc<ADC> {
470 #[inline(always)]
471 fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime) {
472 self.set_channel_sample_time(chan, sample_time);
473 }
474 #[inline(always)]
475 fn set_regular_sequence(&mut self, channels: &[u8]) {
476 self.set_regular_sequence(channels);
477 }
478 #[inline(always)]
479 fn set_continuous_mode(&mut self, continuous: bool) {
480 self.set_continuous_mode(continuous);
481 }
482 #[inline(always)]
483 fn set_discontinuous_mode(&mut self, channels: Option<u8>) {
484 self.set_discontinuous_mode(channels);
485 }
486}
487
488impl<ADC: Instance, WORD, PIN> OneShot<ADC, WORD, PIN> for Adc<ADC>
489where
490 WORD: From<u16>,
491 PIN: Channel<ADC, ID = u8>,
492{
493 type Error = ();
494
495 fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
496 let res = self.convert(PIN::channel());
497 Ok(res.into())
498 }
499}
500
501impl Adc<pac::ADC1> {
502 fn read_aux(&mut self, chan: u8) -> u16 {
503 let tsv_enabled = matches!(chan, TEMP_CHANNEL | VREF_CHANNEL) && self.enable_temp_vref();
504
505 let val = self.convert(chan);
506
507 if tsv_enabled {
508 self.disable_temp_vref();
509 }
510
511 val
512 }
513
514 pub fn enable_temp_vref(&mut self) -> bool {
521 if !self.is_temp_vref_enabled() {
522 self.rb.cr2().modify(|_, w| w.tsvrefe().set_bit());
523
524 delay(self.sysclk.raw() / 80_000);
529 true
530 } else {
531 false
532 }
533 }
534
535 pub fn disable_temp_vref(&mut self) {
540 self.rb.cr2().modify(|_, w| w.tsvrefe().clear_bit());
541 }
542
543 pub fn is_temp_vref_enabled(&self) -> bool {
544 self.rb.cr2().read().tsvrefe().bit_is_set()
545 }
546
547 pub fn read_temp(&mut self) -> i32 {
563 const AVG_SLOPE: i32 = 43;
568 const V_25: i32 = 1430;
569
570 let prev_cfg = self.save_cfg();
571
572 let sample_time = match self.adcclk.raw() {
576 0..=1_200_000 => SampleTime::T_1,
577 1_200_001..=1_500_000 => SampleTime::T_7,
578 1_500_001..=2_400_000 => SampleTime::T_13,
579 2_400_001..=3_100_000 => SampleTime::T_28,
580 3_100_001..=4_000_000 => SampleTime::T_41,
581 4_000_001..=5_000_000 => SampleTime::T_55,
582 5_000_001..=14_000_000 => SampleTime::T_71,
583 _ => SampleTime::T_239,
584 };
585
586 self.set_sample_time(sample_time);
587 let val_temp: i32 = self.read_aux(TEMP_CHANNEL).into();
588 let val_vref: i32 = self.read_aux(VREF_CHANNEL).into();
589 let v_sense = val_temp * 1200 / val_vref;
590
591 self.restore_cfg(prev_cfg);
592
593 (V_25 - v_sense) * 10 / AVG_SLOPE + 25
594 }
595
596 pub fn read_vref(&mut self) -> u16 {
608 self.read_aux(VREF_CHANNEL)
609 }
610}
611
612pub struct AdcPayload<ADC, PINS, MODE> {
613 adc: Adc<ADC>,
614 pins: PINS,
615 _mode: PhantomData<MODE>,
616}
617
618pub trait ChannelTimeSequence {
619 fn set_channel_sample_time(&mut self, chan: u8, sample_time: SampleTime);
621 fn set_regular_sequence(&mut self, channels: &[u8]);
625 fn set_continuous_mode(&mut self, continuous: bool);
629 fn set_discontinuous_mode(&mut self, channels_count: Option<u8>);
633}
634
635pub trait SetChannels<PINS>: ChannelTimeSequence {
655 fn set_samples(&mut self);
656 fn set_sequence(&mut self);
657}
658
659pub type AdcDma<ADC, PINS, MODE, CHANNEL> = RxDma<AdcPayload<ADC, PINS, MODE>, CHANNEL>;
660
661macro_rules! adcdma {
662 ($ADCX:ty: (
663 $rxdma:ident,
664 $dmarxch:ty,
665 )) => {
666 pub type $rxdma<PINS, MODE> = AdcDma<$ADCX, PINS, MODE, $dmarxch>;
667
668 impl<PINS, MODE> Receive for AdcDma<$ADCX, PINS, MODE, $dmarxch> {
669 type RxChannel = $dmarxch;
670 type TransmittedWord = u16;
671 }
672
673 impl<PINS> TransferPayload for AdcDma<$ADCX, PINS, Continuous, $dmarxch> {
674 fn start(&mut self) {
675 self.channel.start();
676 self.payload.adc.rb.cr2().modify(|_, w| w.cont().set_bit());
677 self.payload.adc.rb.cr2().modify(|_, w| w.adon().set_bit());
678 }
679 fn stop(&mut self) {
680 self.channel.stop();
681 self.payload
682 .adc
683 .rb
684 .cr2()
685 .modify(|_, w| w.cont().clear_bit());
686 }
687 }
688
689 impl<PINS> TransferPayload for AdcDma<$ADCX, PINS, Scan, $dmarxch> {
690 fn start(&mut self) {
691 self.channel.start();
692 self.payload.adc.rb.cr2().modify(|_, w| w.adon().set_bit());
693 }
694 fn stop(&mut self) {
695 self.channel.stop();
696 }
697 }
698
699 impl Adc<$ADCX> {
700 pub fn with_dma<PIN>(
701 mut self,
702 pins: PIN,
703 dma_ch: $dmarxch,
704 ) -> AdcDma<$ADCX, PIN, Continuous, $dmarxch>
705 where
706 PIN: Channel<$ADCX, ID = u8>,
707 {
708 self.rb.cr1().modify(|_, w| w.discen().clear_bit());
709 self.rb
710 .cr2()
711 .modify(|_, w| w.align().variant(self.align.into()));
712 self.set_channel_sample_time(PIN::channel(), self.sample_time);
713 self.rb
714 .sqr3()
715 .modify(|_, w| unsafe { w.sq1().bits(PIN::channel()) });
716 self.rb.cr2().modify(|_, w| w.dma().set_bit());
717
718 let payload = AdcPayload {
719 adc: self,
720 pins,
721 _mode: PhantomData,
722 };
723 RxDma {
724 payload,
725 channel: dma_ch,
726 }
727 }
728
729 pub fn with_scan_dma<PINS>(
730 mut self,
731 pins: PINS,
732 dma_ch: $dmarxch,
733 ) -> AdcDma<$ADCX, PINS, Scan, $dmarxch>
734 where
735 Self: SetChannels<PINS>,
736 {
737 self.rb.cr2().modify(|_, w| {
738 w.adon().clear_bit();
739 w.dma().clear_bit();
740 w.cont().clear_bit();
741 w.align().variant(self.align.into())
742 });
743 self.rb
744 .cr1()
745 .modify(|_, w| w.scan().set_bit().discen().clear_bit());
746 self.set_samples();
747 self.set_sequence();
748 self.rb
749 .cr2()
750 .modify(|_, w| w.dma().set_bit().adon().set_bit());
751
752 let payload = AdcPayload {
753 adc: self,
754 pins,
755 _mode: PhantomData,
756 };
757 RxDma {
758 payload,
759 channel: dma_ch,
760 }
761 }
762 }
763
764 impl<PINS> AdcDma<$ADCX, PINS, Continuous, $dmarxch>
765 where
766 Self: TransferPayload,
767 {
768 pub fn split(mut self) -> (Adc<$ADCX>, PINS, $dmarxch) {
769 self.stop();
770
771 let AdcDma { payload, channel } = self;
772 payload.adc.rb.cr2().modify(|_, w| w.dma().clear_bit());
773 payload.adc.rb.cr1().modify(|_, w| w.discen().set_bit());
774
775 (payload.adc, payload.pins, channel)
776 }
777 }
778
779 impl<PINS> AdcDma<$ADCX, PINS, Scan, $dmarxch>
780 where
781 Self: TransferPayload,
782 {
783 pub fn split(mut self) -> (Adc<$ADCX>, PINS, $dmarxch) {
784 self.stop();
785
786 let AdcDma { payload, channel } = self;
787 payload.adc.rb.cr2().modify(|_, w| w.dma().clear_bit());
788 payload.adc.rb.cr1().modify(|_, w| w.discen().set_bit());
789 payload.adc.rb.cr1().modify(|_, w| w.scan().clear_bit());
790
791 (payload.adc, payload.pins, channel)
792 }
793 }
794
795 impl<B, PINS, MODE> crate::dma::CircReadDma<B, u16> for AdcDma<$ADCX, PINS, MODE, $dmarxch>
796 where
797 Self: TransferPayload,
798 &'static mut [B; 2]: WriteBuffer<Word = u16>,
799 B: 'static,
800 {
801 fn circ_read(mut self, mut buffer: &'static mut [B; 2]) -> CircBuffer<B, Self> {
802 let (ptr, len) = unsafe { buffer.write_buffer() };
805 self.channel.set_peripheral_address(
806 unsafe { (*<$ADCX>::ptr()).dr().as_ptr() as u32 },
807 false,
808 );
809 self.channel.set_memory_address(ptr as u32, true);
810 self.channel.set_transfer_length(len);
811
812 atomic::compiler_fence(Ordering::Release);
813
814 self.channel.ch().cr().modify(|_, w| {
815 w.mem2mem().clear_bit();
816 w.pl().medium();
817 w.msize().bits16();
818 w.psize().bits16();
819 w.circ().set_bit();
820 w.dir().clear_bit()
821 });
822
823 self.start();
824
825 CircBuffer::new(buffer, self)
826 }
827 }
828
829 impl<B, PINS, MODE> crate::dma::ReadDma<B, u16> for AdcDma<$ADCX, PINS, MODE, $dmarxch>
830 where
831 Self: TransferPayload,
832 B: WriteBuffer<Word = u16>,
833 {
834 fn read(mut self, mut buffer: B) -> Transfer<W, B, Self> {
835 let (ptr, len) = unsafe { buffer.write_buffer() };
838 self.channel.set_peripheral_address(
839 unsafe { (*<$ADCX>::ptr()).dr().as_ptr() as u32 },
840 false,
841 );
842 self.channel.set_memory_address(ptr as u32, true);
843 self.channel.set_transfer_length(len);
844
845 atomic::compiler_fence(Ordering::Release);
846 self.channel.ch().cr().modify(|_, w| {
847 w.mem2mem().clear_bit();
848 w.pl().medium();
849 w.msize().bits16();
850 w.psize().bits16();
851 w.circ().clear_bit();
852 w.dir().clear_bit()
853 });
854 self.start();
855
856 Transfer::w(buffer, self)
857 }
858 }
859 };
860}
861
862adcdma! {
863 pac::ADC1: (
864 AdcDma1,
865 dma1::C1,
866 )
867}
868
869#[cfg(all(feature = "stm32f103", any(feature = "high", feature = "xl")))]
870adcdma! {
871 pac::ADC3: (
872 AdcDma3,
873 dma2::C5,
874 )
875}