Skip to main content

pic32_hal/
adc.rs

1//! 10-bit Analog-to-Digital Converter (ADC)
2
3use crate::pac::ADC;
4use core::marker::PhantomData;
5
6/// Marker for unsigned 32-bit formats
7pub struct Unsigned32;
8
9/// Marker for signed 32-bit formats
10pub struct Signed32;
11
12/// Marker for unsigned 16-bit formats
13pub struct Unsigned16;
14
15/// Marker for signed 16-bit formats
16pub struct Signed16;
17
18/// Conversion trigger configuration
19#[repr(u8)]
20#[derive(Clone, Copy, Debug)]
21pub enum ConversionTrigger {
22    /// Internal counter ends sampling and starts conversion (auto convert).
23    /// Sample time: u8_value * Tad, u8_value must be greater than 0 and less than 32.
24    Auto(u8), // SSRC = 0b111
25
26    /// CTMU ends sampling and starts conversion
27    Cmtu, // SSRC = 0b011,
28
29    /// Timer 3 period match ends sampling and starts conversion
30    Timer3, // SSRC = 0b010,
31
32    ///  Active transition on INT0 pin ends sampling and starts conversion
33    Int0, // SSRC = 0b001,
34
35    /// Clearing SAMP bit ends sampling and starts conversion (manual)
36    Manual, // SSRC = 0b000,
37}
38
39/// Voltage reference configuration
40#[repr(u8)]
41#[derive(Clone, Copy, Debug)]
42pub enum VoltageReference {
43    /// Vrefh = AVDD, Vrefl = AVSS
44    AvddAvss = 0b000,
45    /// Vrefh = external Vref+ pin, Vrefl = AVSS
46    ExtAvss = 0b001,
47    /// Vrefh = AVDD, Vrefl = external Vref- pin
48    AvddExt = 0b010,
49    /// Vrefh = external Vref+ pin, Vrefl = external Vref- pin
50    ExtExt = 0b11,
51}
52
53/// ADC result buffer mode
54#[derive(Clone, Copy, Debug)]
55pub enum ResultBufferMode {
56    /// Buffer configured as one 16-word buffer ADC1BUFF-ADC1BUF0
57    SingleBuffer,
58    /// Buffer configured as two 8-word buffers, ADC1BUF7-ADC1BUF0, ADC1BUFF-ADCBUF8
59    DoubleBuffer,
60}
61
62/// ADC Conversion clock configuration
63#[derive(Debug)]
64pub enum ConversionClock {
65    /// Half of the FRC clock frequency
66    Frc,
67    /// From peripheral bus clock: PB / (2 * u8_value + 1)
68    Pb(u8),
69}
70
71/// Negative input selection
72#[derive(Clone, Copy, Debug)]
73#[repr(u8)]
74pub enum NegativeInput {
75    /// Low ADC reference voltage Vrefl
76    Vrefl = 0b0,
77    /// Analog input AN1
78    An1 = 0b1,
79}
80
81/// Input scan configuration
82#[derive(Debug)]
83pub enum InputScan {
84    /// Scan mode disabled.
85    /// The u8 value selects the analog input.
86    Off(u8),
87
88    /// Scan mode enabled.
89    /// The u32 value is a bit mask for selecting the inputs to be scanned.
90    On(u32),
91}
92
93/// ADC configuration
94pub struct AdcConfiguration {
95    conversion_trigger: ConversionTrigger,
96    auto_sample: bool,
97    voltage_reference: VoltageReference,
98    offset_calibration: bool,
99    conversions_per_irq: u8,
100    alt_sample_mode: bool,
101    result_buffer_mode: ResultBufferMode,
102    conversion_clock: ConversionClock,
103}
104
105impl Default for AdcConfiguration {
106    /// Create a configuration for manually sampling a single channel
107    fn default() -> Self {
108        AdcConfiguration {
109            conversion_trigger: ConversionTrigger::Auto(31),
110            auto_sample: false,
111            voltage_reference: VoltageReference::AvddAvss,
112            offset_calibration: false,
113            conversions_per_irq: 1,
114            alt_sample_mode: false,
115            result_buffer_mode: ResultBufferMode::SingleBuffer,
116            conversion_clock: ConversionClock::Frc,
117        }
118    }
119}
120
121impl AdcConfiguration {
122    /// Set the conversion trigger. Panics if the a a value outside 1..., 31 is indicated
123    /// for auto conversion
124    pub fn conversion_trigger(&mut self, conversion_trigger: ConversionTrigger) -> &mut Self {
125        if let ConversionTrigger::Auto(delay) = conversion_trigger {
126            assert!(delay > 0 && delay <= 31);
127        }
128        self.conversion_trigger = conversion_trigger;
129        self
130    }
131
132    /// Activate or deactivate automatic sampling
133    pub fn auto_sample(&mut self, auto_sample: bool) -> &mut Self {
134        self.auto_sample = auto_sample;
135        self
136    }
137    /// Configure the voltage reference sources
138    pub fn voltage_reference(&mut self, voltage_reference: VoltageReference) -> &mut Self {
139        self.voltage_reference = voltage_reference;
140        self
141    }
142
143    /// Activate or deactivate the offset calibration mode
144    pub fn offset_calibration(&mut self, offset_calibration: bool) -> &mut Self {
145        self.offset_calibration = offset_calibration;
146        self
147    }
148
149    /// Set the number of conversions per IRQ. The value must be between 1 and 16; otherwise,
150    /// this method will panic.
151    pub fn conversions_per_irq(&mut self, conversions_per_irq: u8) -> &mut Self {
152        assert!(conversions_per_irq > 0 && conversions_per_irq <= 16);
153        self.conversions_per_irq = conversions_per_irq;
154        self
155    }
156
157    /// Activate or deactivate the Alternate Sample Mode
158    pub fn alt_sample_mode(&mut self, alt_sample_mode: bool) -> &mut Self {
159        self.alt_sample_mode = alt_sample_mode;
160        self
161    }
162
163    /// Set result buffer mode
164    pub fn result_buffer_mode(&mut self, result_buffer_mode: ResultBufferMode) -> &mut Self {
165        self.result_buffer_mode = result_buffer_mode;
166        self
167    }
168
169    /// Set the conversion clock
170    pub fn conversion_clock(&mut self, conversion_clock: ConversionClock) -> &mut Self {
171        self.conversion_clock = conversion_clock;
172        self
173    }
174}
175
176pub struct Adc<F> {
177    adc: ADC,
178    _format: PhantomData<F>,
179}
180
181impl Adc<Unsigned32> {
182    /// Create an Adc instance (unsigned 32-bit data format).
183    /// `fractional == true` selects a fractional number format.
184    pub fn new_u32(adc: ADC, fractional: bool) -> Self {
185        let mut adc = Adc {
186            adc,
187            _format: PhantomData,
188        };
189        adc.init(0b100, fractional);
190        adc
191    }
192
193    /// Read from the ADC buffer.
194    /// `index` must be less than or equal to 15.
195    pub fn read(&self, index: usize) -> u32 {
196        let regs = unsafe { core::slice::from_raw_parts(self.adc.buf0.as_ptr(), 16 * 4) };
197        regs[4 * index]
198    }
199}
200
201impl Adc<Signed32> {
202    /// Create an Adc instance (signed 32-bit data format).
203    /// `fractional == true` selects a fractional number format.
204    pub fn new_i32(adc: ADC, fractional: bool) -> Self {
205        let mut adc = Adc {
206            adc,
207            _format: PhantomData,
208        };
209        adc.init(0b101, fractional);
210        adc
211    }
212
213    /// Read from the ADC buffer.
214    /// `index` must be less than or equal to 15.
215    pub fn read(&self, index: usize) -> i32 {
216        let regs =
217            unsafe { core::slice::from_raw_parts(self.adc.buf0.as_ptr() as *const i32, 16 * 4) };
218        regs[4 * index]
219    }
220}
221
222impl Adc<Unsigned16> {
223    /// Create an Adc instance (unsigned 16-bit data format).
224    /// `fractional == true` selects a fractional number format.
225    pub fn new_u16(adc: ADC, fractional: bool) -> Self {
226        let mut adc = Adc {
227            adc,
228            _format: PhantomData,
229        };
230        adc.init(0b000, fractional);
231        adc
232    }
233
234    /// Read from the ADC buffer.
235    /// `index` must be less than or equal to 15.
236    pub fn read(&self, index: usize) -> u16 {
237        let regs =
238            unsafe { core::slice::from_raw_parts(self.adc.buf0.as_ptr() as *const u16, 16 * 8) };
239        regs[8 * index]
240    }
241}
242
243impl Adc<Signed16> {
244    /// Create an Adc instance (signed 16-bit data format).
245    /// `fractional == true` selects a fractional number format.
246    pub fn new_i16(adc: ADC, fractional: bool) -> Self {
247        let mut adc = Adc {
248            adc,
249            _format: PhantomData,
250        };
251        adc.init(0b001, fractional);
252        adc
253    }
254
255    /// Read from the ADC buffer.
256    /// `index` must be less than or equal to 15.
257    pub fn read(&self, index: usize) -> i16 {
258        let regs =
259            unsafe { core::slice::from_raw_parts(self.adc.buf0.as_ptr() as *const i16, 16 * 8) };
260        regs[8 * index]
261    }
262}
263
264impl<F> Adc<F> {
265    /// Set format and turn off
266    fn init(&mut self, format: u8, fractional: bool) {
267        let form = if fractional { format | 0b010 } else { format };
268        self.adc
269            .con1
270            .modify(|_, w| unsafe { w.on().clear_bit().form().bits(form) });
271    }
272
273    /// configure and activate the ADC
274    pub fn configure(&mut self, config: &AdcConfiguration) {
275        let (ssrc, samc) = match config.conversion_trigger {
276            ConversionTrigger::Auto(samc) => (0b111, samc),
277            ConversionTrigger::Cmtu => (0b011, 0),
278            ConversionTrigger::Timer3 => (0b010, 0),
279            ConversionTrigger::Int0 => (0b001, 0),
280            ConversionTrigger::Manual => (0b000, 0),
281        };
282        unsafe {
283            self.adc.con1clr.write_with_zero(|w| w.on().bit(true));
284        }
285        self.adc
286            .con1
287            .modify(|_, w| unsafe { w.ssrc().bits(ssrc).asam().bit(config.auto_sample) });
288        self.adc.con2.modify(|_, w| unsafe {
289            w.vcfg()
290                .bits(config.voltage_reference as u8)
291                .offcal()
292                .bit(config.offset_calibration)
293                .bufm()
294                .bit(config.result_buffer_mode as u8 != 0)
295                .smpi()
296                .bits(config.conversions_per_irq)
297                .alts()
298                .bit(config.alt_sample_mode)
299        });
300        let (adrc, adcs) = match config.conversion_clock {
301            ConversionClock::Pb(adcs) => (false, adcs),
302            ConversionClock::Frc => (true, 0),
303        };
304        self.adc
305            .con3
306            .modify(|_, w| unsafe { w.adrc().bit(adrc).samc().bits(samc).adcs().bits(adcs) });
307        unsafe {
308            self.adc.con1set.write_with_zero(|w| w.on().set_bit());
309        }
310    }
311
312    /// Select positive input for sample A
313    pub fn select_pos_input(&mut self, input: InputScan) {
314        match input {
315            InputScan::Off(channel) => {
316                self.adc
317                    .chs
318                    .modify(|_, w| unsafe { w.ch0sa().bits(channel) });
319                unsafe {
320                    self.adc.con2clr.write_with_zero(|w| w.cscna().bit(true));
321                }
322            }
323            InputScan::On(mask) => {
324                self.adc.cssl.write(|w| unsafe { w.bits(mask) });
325                unsafe {
326                    self.adc.con2set.write_with_zero(|w| w.cscna().bit(true));
327                }
328            }
329        }
330    }
331
332    /// Select negative input for sample A
333    pub fn select_neg_input(&mut self, input: NegativeInput) {
334        self.adc.chs.modify(|_, w| w.ch0na().bit(input as u8 != 0));
335    }
336
337    /// Select positive input for sample B (alternate sample)
338    pub fn select_pos_alt_input(&mut self, input: u8) {
339        self.adc.chs.modify(|_, w| unsafe { w.ch0sb().bits(input) });
340    }
341
342    /// Select negative input for sample B (alternate sample)
343    pub fn select_neg_alt_input(&mut self, input: NegativeInput) {
344        self.adc.chs.modify(|_, w| w.ch0nb().bit(input as u8 != 0));
345    }
346
347    /// Manually start a sampling period.
348    /// Use only when auto_sample is not active.
349    pub fn start_sampling(&mut self) {
350        self.adc
351            .con1
352            .modify(|_, w| w.samp().set_bit().done().clear_bit());
353    }
354
355    /// Manually start a conversion period.
356    /// Use only when the ConversionTrigger is Manual.
357    pub fn start_conversion(&mut self) {
358        unsafe {
359            self.adc.con1clr.write_with_zero(|w| w.samp().set_bit());
360        }
361    }
362
363    /// Check if conversion is complete.
364    /// To be used when sampling is stated manually.
365    pub fn done(&self) -> bool {
366        self.adc.con1.read().done().bit()
367    }
368
369    /// return the ADC consuming the Adc instance
370    pub fn free(self) -> ADC {
371        // turn ADC off
372        self.adc.con1clr.write(|w| w.on().bit(true));
373        self.adc
374    }
375}