1use embassy_hal_internal::{Peri, PeripheralType};
2use embassy_time::{Duration, block_for};
3
4use crate::{
5 adc::{AdcChannelConfig, AverageMode, Resolution},
6 constants::{TSN_INTERCEPT, TSN_SLOPE},
7 gpio::{Flex, Pin},
8 pac,
9};
10
11#[cfg(adc12)]
12use pac::adc12::{self as adc_pac};
13#[cfg(adc14)]
14use pac::adc14::{self as adc_pac};
15#[cfg(adc16)]
16use pac::adc16::{self as adc_pac};
17
18use adc_pac::regs::Adans;
19
20#[cfg(adc16)]
21use pac::adc16::{regs::Addiscr, vals::Vrefadcg};
22
23pub struct Temperature;
30
31pub struct Vref;
38
39#[allow(private_bounds)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub struct AdcPin {
46 adc_channel: u8,
47}
48
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub struct AdcSequence<const N: usize> {
57 channels: [u8; N],
58}
59
60#[allow(private_bounds)]
62pub trait AdcInputPin: SealedAdcInputPin {
63 const CHANNEL: u8;
65}
66
67pub(crate) trait SealedAdcInputPin: Pin + PeripheralType {}
69
70#[allow(private_bounds)]
79pub trait AdcChannel<R>: SealedAdcChannel<R> {}
80
81pub(super) trait SealedAdcChannel<R> {
82 fn enable<I: super::Instance>(&self, config: AdcChannelConfig);
83 fn disable<I: super::Instance>(&self);
84 fn read_one<I: super::Instance>(&self) -> R;
85 fn address<I: super::Instance>(&self) -> *const R;
86}
87
88#[allow(private_bounds)]
89impl AdcPin {
90 #[inline]
92 pub fn new<'d, P: AdcInputPin>(pin: Peri<'d, P>) -> Self {
93 let mut flex = Flex::new_basic(pin);
94 flex.set_as_analog();
95
96 #[cfg(feature = "strict-assert")]
97 assert!(P::CHANNEL <= 14 || (P::CHANNEL >= 16 && P::CHANNEL < 25));
98
99 let adc_channel = P::CHANNEL;
100
101 Self { adc_channel }
102 }
103}
104
105impl<const N: usize> AdcSequence<N> {
106 pub fn new(pins: [AdcPin; N]) -> Self {
108 #[cfg(feature = "strict-assert")]
110 assert!(N <= 26);
111
112 let channels: [u8; N] = pins.map(|pin| pin.adc_channel);
117
118 Self { channels }
119 }
120
121 #[inline]
123 pub fn channels(&self) -> &[u8] {
124 &self.channels
125 }
126}
127
128impl<const N: usize> AdcChannel<[u16; N]> for AdcSequence<N> {}
129
130impl<const N: usize> SealedAdcChannel<[u16; N]> for AdcSequence<N> {
131 #[inline]
132 fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
133 trace!("ADC14: enable_channel(sequence)");
137
138 let adc = I::regs();
139
140 adc.adexicr().modify(|w| {
141 w.set_ocsa(false);
142 w.set_tssa(false);
143 });
144
145 for channel in self.channels.iter() {
146 trace!(" {} enable", channel);
147
148 let index = *channel as usize / 16;
149 let offset = *channel as usize % 16;
150
151 adc.adansa(index).write(|w| w.set_ans(offset, true));
152
153 if *channel <= 14 {
154 adc.adsstr(*channel as _).write_value(config.sample_time);
155 } else {
156 adc.adsstrl().write_value(config.sample_time);
157 }
158 }
159
160 if config.average_mode != AverageMode::Off {
161 for channel in self.channels.iter() {
162 trace!(" {} add/avg", channel);
163
164 let index = *channel as usize / 16;
165 let offset = *channel as usize % 16;
166
167 adc.adads(index).write(|w| w.set_ads(offset, true));
168 }
169 }
170 }
171
172 #[inline]
173 fn disable<I: super::Instance>(&self) {
174 trace!("ADC14: disable_channel(sequence)");
175
176 let adc = I::regs();
177
178 if adc.adcsr().read().adst() {
179 warn!("ADC14: Trying to disable sequence with a conversion running");
180 }
181
182 adc.adansa(0).write_value(Adans(0));
184 adc.adansa(1).write_value(Adans(0));
185 }
186
187 #[inline]
188 fn read_one<I: super::Instance>(&self) -> [u16; N] {
189 let adc = I::regs();
190
191 self.channels.map(|chan| adc.addr(chan as _).read())
192 }
193
194 #[inline]
195 fn address<I: super::Instance>(&self) -> *const [u16; N] {
196 todo!("DMA xfers for sequences not yet supported")
203 }
204}
205
206impl AdcChannel<u16> for AdcPin {}
207
208impl SealedAdcChannel<u16> for AdcPin {
209 #[inline]
210 fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
211 let adc = I::regs();
212
213 trace!("ADC14: enable_channel({})", self.adc_channel);
214
215 adc.adexicr().modify(|w| {
216 w.set_ocsa(false);
217 w.set_tssa(false);
218 });
219
220 let index = self.adc_channel as usize / 16;
221 let offset = self.adc_channel as usize % 16;
222
223 adc.adansa(index).write(|w| w.set_ans(offset, true));
224
225 if config.average_mode != AverageMode::Off {
226 adc.adads(index).write(|w| w.set_ads(offset, true))
227 }
228
229 if self.adc_channel <= 14 {
230 adc.adsstr(self.adc_channel as _)
231 .write_value(config.sample_time);
232 } else {
233 adc.adsstrl().write_value(config.sample_time);
234 }
235 }
236
237 #[inline]
238 fn disable<I: super::Instance>(&self) {
239 let adc = I::regs();
240
241 trace!("ADC14: disable_channel({})", self.adc_channel);
242
243 if adc.adcsr().read().adst() {
244 warn!(
245 "ADC14: Trying to disable chan={} with a conversion running",
246 self.adc_channel
247 );
248 }
249
250 let index = self.adc_channel as usize / 16;
251 let offset = self.adc_channel as usize % 16;
252
253 adc.adansa(index).modify(|w| w.set_ans(offset, false));
254 }
255
256 #[inline]
257 fn read_one<I: super::Instance>(&self) -> u16 {
258 let adc = I::regs();
259
260 trace!("ADC14: read_one({})", self.adc_channel);
261
262 adc.addr(self.adc_channel as _).read()
263 }
264
265 #[inline]
266 fn address<I: super::Instance>(&self) -> *const u16 {
267 let adc = I::regs();
268 adc.addr(self.adc_channel as _).as_ptr()
269 }
270}
271
272impl Temperature {
273 pub fn raw_to_celsius(&self, v_s: u16, vref_mv: u16, width: Resolution) -> i32 {
275 let tsn = pac::TSN;
278 let v_1 = tsn.tscdr().read();
279
280 let calibration_width = cfg_select! {
281 adc12 => 4096, adc14 => 4096, adc16 => 32768, _ => compile_error!()
285 };
286
287 let measurement_width = match width {
288 #[cfg(any(adc12, adc14))]
289 Resolution::_12bit => 4_096,
290 #[cfg(adc14)]
291 Resolution::_14bit => 16_384,
292 #[cfg(adc16)]
293 Resolution::_16bit => 32_768,
294 };
295
296 let v_1 = (u64::from(v_1) * 3_300_000) / calibration_width;
297 let v_s = (u64::from(v_s) * u64::from(vref_mv) * 1_000) / measurement_width;
298
299 i32::try_from(((v_s - v_1) as i64 / i64::from(TSN_SLOPE)) + i64::from(TSN_INTERCEPT))
300 .expect("Temperature reading overflowed the bounds of an i32")
301 }
302
303 pub fn raw_to_celsius_float(&self, v_s: u16, vref_mv: u16, width: Resolution) -> f32 {
305 let tsn = pac::TSN;
308 let v_1 = tsn.tscdr().read();
309
310 let calibration_width = cfg_select! {
311 adc12 => 4096, adc14 => 4096, adc16 => 32768, _ => compile_error!()
315 };
316
317 let measurement_width = match width {
318 #[cfg(any(adc12, adc14))]
319 Resolution::_12bit => 4_096,
320 #[cfg(adc14)]
321 Resolution::_14bit => 16_384,
322 #[cfg(adc16)]
323 Resolution::_16bit => 32_768,
324 };
325
326 let v_1 = (u64::from(v_1) * 3_300_000) / calibration_width;
327 let v_s = (u64::from(v_s) * u64::from(vref_mv) * 1_000) / measurement_width;
328
329 let v_1 = v_1 * 100;
330 let v_s = v_s * 100;
331 let intercept = TSN_INTERCEPT * 100;
332
333 (((v_s - v_1) as i64 / i64::from(TSN_SLOPE)) + i64::from(intercept)) as f32 / 100.0
334 }
335}
336
337impl AdcChannel<u16> for Temperature {}
338
339impl SealedAdcChannel<u16> for Temperature {
340 #[inline]
341 fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
342 let adc = I::regs();
343
344 trace!("ADC14: enable_channel(TEMPERATURE)");
345
346 if adc.adcsr().read().adst() {
347 warn!("ADC14: Trying to enable temp with a conversion running");
348 }
349
350 adc.adsstrt().write_value(config.sample_time);
351
352 #[cfg(any(adc12, adc14))]
353 {
354 adc.adhvrefcnt().modify(|r| {
355 use adc_pac::vals::Hvsel;
356
357 r.set_hvsel(Hvsel::Avcc0);
358 });
359 }
360
361 #[cfg(adc16)]
362 {
363 adc.addiscr().write_value(Addiscr(0xF));
364
365 adc.vrefampcnt().modify(|r| {
366 r.set_vrefadcg(Vrefadcg::_11);
367 r.set_bgren(true);
368 });
369
370 block_for(Duration::from_micros(2000));
372
373 adc.vrefampcnt().modify(|r| {
374 r.set_oldeten(true);
375 r.set_vrefadcen(true);
376 r.set_adslp(false);
377 });
378 block_for(Duration::from_micros(2000));
379 }
380
381 adc.adexicr().write(|w| w.set_tssa(true));
382 block_for(Duration::from_micros(2000));
383
384 if config.average_mode != AverageMode::Off {
385 todo!()
386 }
387 }
388
389 #[inline]
390 fn disable<I: super::Instance>(&self) {
391 let adc = I::regs();
392
393 trace!("ADC14: disable_channel(TEMPERATURE)");
394
395 if adc.adcsr().read().adst() {
396 warn!("ADC14: Trying to disable temp with a conversion running");
397 }
398
399 adc.adexicr().modify(|w| w.set_tssa(false));
400 }
401
402 #[inline]
403 fn read_one<I: super::Instance>(&self) -> u16 {
404 let adc = I::regs();
405
406 trace!("ADC14: read_one(TEMPERATURE)");
407 adc.adtsdr().read()
408 }
409
410 #[inline]
411 fn address<I: super::Instance>(&self) -> *const u16 {
412 let adc = I::regs();
413 adc.adtsdr().as_ptr()
414 }
415}
416
417impl AdcChannel<u16> for Vref {}
418
419impl SealedAdcChannel<u16> for Vref {
420 #[inline]
421 fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
422 let adc = I::regs();
423
424 trace!("ADC14: enable_channel(VREF)");
425
426 if adc.adcsr().read().adst() {
427 warn!("ADC14: Trying to enable Vref with a conversion running");
428 }
429
430 adc.adsstro().write_value(config.sample_time);
431
432 adc.adexicr().modify(|w| w.set_ocsa(true));
433
434 if config.average_mode != AverageMode::Off {
435 todo!()
436 }
437 }
438
439 #[inline]
440 fn disable<I: super::Instance>(&self) {
441 let adc = I::regs();
442
443 trace!("ADC14: disable_channel(VREF)");
444
445 if adc.adcsr().read().adst() {
446 warn!("ADC14: Trying to disable Vref with a conversion running");
447 }
448
449 adc.adexicr().modify(|w| w.set_ocsa(false));
450 }
451
452 #[inline]
453 fn read_one<I: super::Instance>(&self) -> u16 {
454 let adc = I::regs();
455
456 trace!("ADC14: read_one(VREF)");
457 adc.adocdr().read()
458 }
459
460 #[inline]
461 fn address<I: super::Instance>(&self) -> *const u16 {
462 let adc = I::regs();
463 adc.adocdr().as_ptr()
464 }
465}
466
467macro_rules! adc_pin {
468 ($channel:literal, $pin:ident) => {
469 impl crate::adc::channel::AdcInputPin for crate::peripherals::$pin {
470 const CHANNEL: u8 = $channel;
471 }
472 impl crate::adc::channel::SealedAdcInputPin for crate::peripherals::$pin {}
473 };
474}
475pub(crate) use adc_pin;