Skip to main content

sam4l/
acifc.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of the SAM4L ACIFC controller.
6//!
7//! See datasheet section "37. Analog Comparator Interface (ACIFC)".
8//!
9//! Overview
10//! -----
11//! The SAM4L Analog Comparator Interface (ACIFC) controls a number of Analog
12//! Comparators (ACs) with identical behavior. Each Analog Comparator compares
13//! two voltages and gives an output depending on this comparison. A specific AC
14//! is referred to as ACx where x is any number from 0 to n and n is the index
15//! of last AC module.
16//!
17//! The number of analog comparators (ACs) available depends on the board pr
18//! microcontroller used.  The SAM4Lmcomes in three different versions: a 48-pin, a
19//! 64-pin and a 100-pin version.  On the 48-pin version, one AC is available.
20//! On the 64-pin version, two ACs are available.  On the 100-pin version, four
21//! ACs are available.
22//! The Hail is an example of a board with the 64-pin version of the SAM4L,
23//! and therefore supports two ACs.
24//! The Imix is an example of a board with the 100-pin version of the SAM4L,
25//! and therefore supports four ACs.
26//! Currently, no version of the SAM4L exists with all the 8 ACs
27//! implemented. Therefore a lot of the defined bitfields remain unused, but
28//! are initialized for a possible future scenario.
29
30// Author: Danilo Verhaert <verhaert@cs.stanford.edu>
31
32use crate::pm;
33use core::cell::Cell;
34use kernel::ErrorCode;
35use kernel::debug;
36use kernel::hil::analog_comparator;
37use kernel::utilities::StaticRef;
38use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
39use kernel::utilities::registers::{
40    ReadOnly, ReadWrite, WriteOnly, register_bitfields, register_structs,
41};
42
43/// Representation of an AC channel on the SAM4L.
44pub struct AcChannel {
45    chan_num: u32,
46}
47
48#[derive(Copy, Clone, Debug)]
49#[repr(u8)]
50pub enum Channel {
51    AC0 = 0x00,
52    AC1 = 0x01,
53    AC2 = 0x02,
54    AC3 = 0x03,
55}
56
57/// Initialization of an AC channel.
58impl AcChannel {
59    /// Create a new AC channel.
60    ///
61    /// - `channel`: Channel enum representing the channel number
62    pub const fn new(channel: Channel) -> AcChannel {
63        AcChannel {
64            chan_num: ((channel as u8) & 0x0F) as u32,
65        }
66    }
67}
68
69register_structs! {
70    AcifcRegisters {
71        (0x000 => ctrl: ReadWrite<u32, Control::Register>),
72        (0x004 => sr: ReadOnly<u32, Status::Register>),
73        (0x008 => _reserved0: [ReadOnly<u32>; 2]),
74        (0x010 => ier: WriteOnly<u32, Interrupt::Register>),
75        (0x014 => idr: WriteOnly<u32, Interrupt::Register>),
76        (0x018 => imr: ReadOnly<u32, Interrupt::Register>),
77        (0x01C => isr: ReadOnly<u32, Interrupt::Register>),
78        (0x020 => icr: WriteOnly<u32, Interrupt::Register>),
79        (0x024 => tr: ReadWrite<u32, Test::Register>),
80        (0x028 => _reserved1: [ReadOnly<u32>; 2]),
81        (0x030 => parameter: ReadOnly<u32, Parameter::Register>),
82        (0x034 => version: ReadOnly<u32>),
83        (0x038 => _reserved2: [ReadOnly<u32>; 18]),
84        (0x080 => confw: [ReadWrite<u32, WindowConfiguration::Register>; 4]),
85        (0x090 => _reserved3: [ReadOnly<u32>; 16]),
86        (0x0D0 => conf: [ReadWrite<u32, ACConfiguration::Register>; 8]),
87        (0x0F0 => @END),
88    }
89}
90
91register_bitfields![u32,
92    Control [
93        /// Analog comparator test mode. Equal to 1 means AC outputs will be
94        /// bypassed with values in AC test register.
95        ACTEST 7,
96        /// This bit is set when an enabled peripheral event is received (called
97        /// by EVENTEN), and starts a single comparison.
98        ESTART 5,
99        /// This bit can be set by the user and starts a single comparison.
100        USTART 4,
101        /// This bit sets ESTART to 1 on receiving a peripheral event from
102        /// another hardware module.
103        EVENTEN 1,
104        /// Enables or disables the ACIFC.
105        EN 0
106    ],
107
108    Status [
109        /// This bit represents an output for the window mode, and reads one
110        /// when the common input voltage is inside the window of the two
111        /// non-common inputs.
112        WFCS3 27,
113        WFCS2 26,
114        WFCS1 25,
115        WFCS0 24,
116        /// ACRDY is set when the AC output is ready. ACCS is set when the
117        /// positive input voltage V_{INP} is greater than the negative input
118        /// voltage V_{INN}.
119        ACRDY7 15,
120        ACCS7 14,
121        ACRDY6 13,
122        ACCS6 12,
123        ACRDY5 11,
124        ACCS5 10,
125        ACRDY4 9,
126        ACCS4 8,
127        ACRDY3 7,
128        ACCS3 6,
129        ACRDY2 5,
130        ACCS2 4,
131        ACRDY1 3,
132        ACCS1 2,
133        ACRDY0 1,
134        ACCS0 0
135    ],
136
137    /// - IER: Writing a one to a bit in this register will set the
138    ///   corresponding bit in IMR.
139    /// - IDR: Writing a one to a bit in this register will clear the
140    ///   corresponding bit in IMR.
141    /// - IMR: Writing a one in any of these bits will enable the corresponding
142    ///   interrupt.
143    /// - ISR: WFINTx shows if a window mode interrupt is pending. SUTINTx shows
144    ///   if a startup time interrupt is pending. ACINTx shows if a normal mode
145    ///   interrupt is pending.
146    /// - ICR: Writing a one to a bit in this register will clear the
147    ///   corresponding bit in ISR and the corresponding interrupt request.
148    Interrupt [
149        WFINT3 27,
150        WFINT2 26,
151        WFINT1 25,
152        WFINT0 24,
153        SUTINT7 15,
154        ACINT7 14,
155        SUTINT6 13,
156        ACINT6 12,
157        SUTINT5 11,
158        ACINT5 10,
159        SUTINT4 9,
160        ACINT4 8,
161        SUTINT3 7,
162        ACINT3 6,
163        SUTINT2 5,
164        ACINT2 4,
165        SUTINT1 3,
166        ACINT1 2,
167        SUTINT0 1,
168        ACINT0 0
169    ],
170
171    Test [
172        /// If equal to one, overrides ACx output with the value of ACTESTx.
173        ACTEST7 7,
174        ACTEST6 6,
175        ACTEST5 5,
176        ACTEST4 4,
177        ACTEST3 3,
178        ACTEST2 2,
179        ACTEST1 1,
180        ACTEST0 0
181    ],
182
183    Parameter [
184        /// If equal to one, window mode x is implemented.
185        WIMPL3 19,
186        WIMPL2 18,
187        WIMPL1 17,
188        WIMPL0 16,
189        /// If equal to one, analog comparator x is implemented.
190        ACIMPL7 7,
191        ACIMPL6 6,
192        ACIMPL5 5,
193        ACIMPL4 4,
194        ACIMPL3 3,
195        ACIMPL2 2,
196        ACIMPL1 1,
197        ACIMPL0 0
198        ],
199
200    WindowConfiguration [
201        /// If equal to one, window mode is enabled.
202        WFEN OFFSET(16) NUMBITS(1) [],
203        /// If equal to one, peripheral event from ACWOUT is enabled.
204        WEVEN OFFSET(11) NUMBITS(1) [],
205        /// Peripheral Event Source Selection for Window Mode
206        WEVSRC OFFSET (8) NUMBITS(3) [
207            AcwoutRisingEdge = 0,
208            AcwoutFallingEdge = 1,
209            AcwoutRisingOrFallingEdge = 2,
210            InsideWindow = 3,
211            OutsideWindow = 4,
212            MeasureDone = 5
213        ],
214            /// Window Mode Interrupt Settings
215        WIS OFFSET(0) NUMBITS (3)[
216            /// Window interrupt as soon as the common input voltage is inside
217            /// the window
218            InterruptInsideWindow = 0,
219            /// Window interrupt as soon as the common input voltage is outside
220            /// the window
221            InterruptOutsideWindow = 1,
222            /// Window interrupt on toggle of ACWOUT
223            InterruptToggleAcwout = 2,
224            /// Window interrupt when evaluation of common input voltage is done
225            InterruptAfterEvaluation = 3,
226            /// Window interrupt when the common input voltage enters the window
227            /// (i.e., rising-edge of ACWOUT)
228            InterruptEnterWindow = 4,
229            /// Window interrupt when the common input voltage leaves the window
230            /// (i.e., falling-edge of ACWOUT)
231            InterruptLeaveWindow = 5
232    ]
233    ],
234
235    ACConfiguration [
236        /// If equal to one, AC is always enabled.
237        ALWAYSON OFFSET(27) NUMBITS(1) [],
238        /// 0: Low-power mode. 1: Fastm ode.
239        FAST OFFSET(26) NUMBITS(1) [],
240        /// Hysteresis voltage value: 0/25/50/75 mV
241        HYS OFFSET(24) NUMBITS(2) [
242            HysteresisVoltage0mV = 0,
243            HysteresisVoltage25mV = 1,
244            HysteresisVoltage50mV = 2,
245            HysteresisVoltage75mV = 3
246        ],
247        /// Setting this to one will output peripheral event when ACOUT is zero.
248        EVENP OFFSET(17) NUMBITS(1) [],
249        /// Setting this to one will output peripheral event when ACOUT is one.
250        EVENN OFFSET(16) NUMBITS(1) [],
251        /// Negative input select. 00: ACANx pint selected, others reserved.
252        INSELN OFFSET(8) NUMBITS(2) [],
253        /// Choose between analog comparator mode.
254        MODE OFFSET(4) NUMBITS(2) [
255            Off = 0,
256            ContinuousMeasurementMode = 1,
257            /// User Triggered Single Measurement Mode
258            UserMode = 2,
259            /// Peripheral Event Single Measurement Mode
260            PeripheralMode = 3
261        ],
262        /// Interrupt settings
263        IS OFFSET(0) NUMBITS(2) [
264            /// When Vinp > Vinn
265            WhenVinpGtVinn = 0,
266            /// When Vinp < Vinn
267            WhenVinpLtVinn = 1,
268            /// On toggle of ACOUT
269            OnToggleOfACOUT = 2,
270            /// When comparison of Vinp and Vinn is done
271            WhenComparisonDone = 3
272        ]
273    ]
274];
275
276const ACIFC_BASE: StaticRef<AcifcRegisters> =
277    unsafe { StaticRef::new(0x40040000 as *const AcifcRegisters) };
278
279pub struct Acifc<'a> {
280    client: Cell<Option<&'a dyn analog_comparator::Client>>,
281}
282
283/// Implement constructor for struct Acifc
284impl<'a> Acifc<'a> {
285    pub const fn new() -> Acifc<'a> {
286        Acifc {
287            client: Cell::new(None),
288        }
289    }
290
291    fn enable_clock(&self) {
292        pm::enable_clock(pm::Clock::PBA(pm::PBAClock::ACIFC));
293    }
294
295    fn disable_clock(&self) {
296        pm::disable_clock(pm::Clock::PBA(pm::PBAClock::ACIFC));
297    }
298
299    /// Enabling the ACIFC by activating the clock and the ACs (Analog
300    /// Comparators). Currently always-on mode is enabled, allowing a
301    /// measurement on an AC to be made quickly after a measurement is
302    /// triggered, without waiting for the AC startup time. The drawback is
303    /// that the AC is always on, leading to a higher power dissipation.
304    fn enable(&self) {
305        let regs = ACIFC_BASE;
306        self.enable_clock();
307        regs.ctrl.write(Control::EN::SET);
308
309        // Enable continuous measurement mode and always-on mode for all the analog comparators
310        regs.conf[0].write(
311            ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
312        );
313        regs.conf[1].write(
314            ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
315        );
316        regs.conf[2].write(
317            ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
318        );
319        regs.conf[3].write(
320            ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
321        );
322
323        // Make sure enabling was succesful
324        let result = regs.ctrl.is_set(Control::EN);
325        if !result {
326            debug!("Failed enabling analog comparator, are you sure the clock is enabled?");
327        }
328    }
329
330    /// Disable the entire ACIFC
331    fn disable(&self) {
332        let regs = ACIFC_BASE;
333        self.disable_clock();
334        regs.ctrl.write(Control::EN::CLEAR);
335    }
336
337    /// Handling of interrupts. Currently set up so that an interrupt fires
338    /// only once when the condition is true (e.g. Vinp > Vinn), and then
339    /// doesn't fire anymore until the condition is false (e.g. Vinp < Vinn).
340    /// This way we won't get a barrage of interrupts as soon as Vinp > Vinn:
341    /// we'll get just one.
342    pub fn handle_interrupt(&self) {
343        let regs = ACIFC_BASE;
344
345        // We check which AC generated the interrupt, and callback to the client accordingly
346        if regs.isr.is_set(Interrupt::ACINT0) {
347            // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
348            if !regs.imr.is_set(Interrupt::ACINT0) {
349                return;
350            }
351
352            // Disable IMR, making sure no more interrupts can occur until we write
353            // to IER
354            regs.idr.write(Interrupt::ACINT0::SET);
355
356            // If Vinp > Vinn, throw an interrupt to the client and set the AC so
357            // that it will throw an interrupt when Vinn < Vinp instead.
358            if !regs.conf[0].is_set(ACConfiguration::IS) {
359                self.client.get().map(|client| {
360                    client.fired(0);
361                });
362                regs.conf[0].modify(ACConfiguration::IS::WhenVinpLtVinn);
363            }
364            // If Vinp < Vinn, set the AC so that it will throw an interrupt when
365            // Vinp > Vinn instead.
366            else {
367                regs.conf[0].modify(ACConfiguration::IS::WhenVinpGtVinn);
368            }
369
370            // Clear the interrupt request
371            regs.icr.write(Interrupt::ACINT0::SET);
372            regs.ier.write(Interrupt::ACINT0::SET);
373        } else if regs.isr.is_set(Interrupt::ACINT1) {
374            // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
375            if !regs.imr.is_set(Interrupt::ACINT1) {
376                return;
377            }
378
379            // Disable IMR, making sure no more interrupts can occur until we write
380            // to IER
381            regs.idr.write(Interrupt::ACINT1::SET);
382
383            // If Vinp > Vinn, throw an interrupt to the client and set the AC so
384            // that it will throw an interrupt when Vinn < Vinp instead.
385            if !regs.conf[1].is_set(ACConfiguration::IS) {
386                self.client.get().map(|client| {
387                    client.fired(1);
388                });
389                regs.conf[1].modify(ACConfiguration::IS::WhenVinpLtVinn);
390            }
391            // If Vinp < Vinn, set the AC so that it will throw an interrupt when
392            // Vinp > Vinn instead.
393            else {
394                regs.conf[1].modify(ACConfiguration::IS::WhenVinpGtVinn);
395            }
396
397            // Clear the interrupt request
398            regs.icr.write(Interrupt::ACINT1::SET);
399            regs.ier.write(Interrupt::ACINT1::SET);
400        } else if regs.isr.is_set(Interrupt::ACINT2) {
401            // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
402            if !regs.imr.is_set(Interrupt::ACINT2) {
403                return;
404            }
405
406            // Disable IMR, making sure no more interrupts can occur until we write
407            // to IER
408            regs.idr.write(Interrupt::ACINT2::SET);
409
410            // If Vinp > Vinn, throw an interrupt to the client and set the AC so
411            // that it will throw an interrupt when Vinn < Vinp instead.
412            if !regs.conf[2].is_set(ACConfiguration::IS) {
413                self.client.get().map(|client| {
414                    client.fired(2);
415                });
416                regs.conf[2].modify(ACConfiguration::IS::WhenVinpLtVinn);
417            }
418            // If Vinp < Vinn, set the AC so that it will throw an interrupt when
419            // Vinp > Vinn instead.
420            else {
421                regs.conf[2].modify(ACConfiguration::IS::WhenVinpGtVinn);
422            }
423
424            // Clear the interrupt request
425            regs.icr.write(Interrupt::ACINT2::SET);
426            regs.ier.write(Interrupt::ACINT2::SET);
427        } else if regs.isr.is_set(Interrupt::ACINT3) {
428            // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
429            if !regs.imr.is_set(Interrupt::ACINT3) {
430                return;
431            }
432
433            // Disable IMR, making sure no more interrupts can occur until we write
434            // to IER
435            regs.idr.write(Interrupt::ACINT3::SET);
436
437            // If Vinp > Vinn, throw an interrupt to the client and set the AC so
438            // that it will throw an interrupt when Vinn < Vinp instead.
439            if !regs.conf[3].is_set(ACConfiguration::IS) {
440                self.client.get().map(|client| {
441                    client.fired(3);
442                });
443                regs.conf[3].modify(ACConfiguration::IS::WhenVinpLtVinn);
444            }
445            // If Vinp < Vinn, set the AC so that it will throw an interrupt when
446            // Vinp > Vinn instead.
447            else {
448                regs.conf[3].modify(ACConfiguration::IS::WhenVinpGtVinn);
449            }
450
451            // Clear the interrupt request
452            regs.icr.write(Interrupt::ACINT3::SET);
453            regs.ier.write(Interrupt::ACINT3::SET);
454        }
455    }
456}
457
458impl<'a> analog_comparator::AnalogComparator<'a> for Acifc<'a> {
459    type Channel = AcChannel;
460
461    /// Do a single comparison
462    fn comparison(&self, channel: &Self::Channel) -> bool {
463        self.enable();
464        let regs = ACIFC_BASE;
465        let result = if channel.chan_num == 0 {
466            regs.sr.is_set(Status::ACCS0)
467        } else if channel.chan_num == 1 {
468            regs.sr.is_set(Status::ACCS1)
469        } else if channel.chan_num == 2 {
470            regs.sr.is_set(Status::ACCS2)
471        } else if channel.chan_num == 3 {
472            regs.sr.is_set(Status::ACCS3)
473        } else {
474            // Should never get here, just making sure
475            self.disable();
476            panic!("PANIC! Please choose a comparator (value of ac) that this chip supports");
477        };
478        result
479    }
480
481    /// Start interrupt-based comparisons
482    fn start_comparing(&self, channel: &Self::Channel) -> Result<(), ErrorCode> {
483        self.enable();
484        let regs = ACIFC_BASE;
485
486        if channel.chan_num == 0 {
487            // Enable interrupts.
488            regs.ier.write(Interrupt::ACINT0::SET);
489            Ok(())
490        } else if channel.chan_num == 1 {
491            // Repeat the same for ac == 1
492            regs.ier.write(Interrupt::ACINT1::SET);
493            Ok(())
494        } else if channel.chan_num == 2 {
495            // Repeat the same for ac == 2
496            regs.ier.write(Interrupt::ACINT2::SET);
497            Ok(())
498        } else if channel.chan_num == 3 {
499            // Repeat the same for ac == 3
500            regs.ier.write(Interrupt::ACINT3::SET);
501            Ok(())
502        } else {
503            // Should never get here, just making sure
504            self.disable();
505            debug!("Please choose a comparator (value of ac) that this chip supports");
506            Err(ErrorCode::INVAL)
507        }
508    }
509
510    /// Stop interrupt-based comparisons
511    fn stop_comparing(&self, channel: &Self::Channel) -> Result<(), ErrorCode> {
512        let regs = ACIFC_BASE;
513
514        if channel.chan_num == 0 {
515            // Disable interrupts.
516            regs.ier.write(Interrupt::ACINT0::CLEAR);
517            Ok(())
518        } else if channel.chan_num == 1 {
519            // Repeat the same for ac == 1
520            regs.ier.write(Interrupt::ACINT1::CLEAR);
521            Ok(())
522        } else if channel.chan_num == 2 {
523            // Repeat the same for ac == 2
524            regs.ier.write(Interrupt::ACINT2::CLEAR);
525            Ok(())
526        } else if channel.chan_num == 3 {
527            // Repeat the same for ac == 3
528            regs.ier.write(Interrupt::ACINT3::CLEAR);
529            Ok(())
530        } else {
531            // Should never get here, just making sure
532            self.disable();
533            debug!("Please choose a comparator (value of ac) that this chip supports");
534            Err(ErrorCode::INVAL)
535        }
536    }
537
538    fn set_client(&self, client: &'a dyn analog_comparator::Client) {
539        self.client.set(Some(client));
540    }
541}