rp2040/
adc.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
5use core::cell::Cell;
6use kernel::hil;
7use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
8use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
9use kernel::utilities::{cells::OptionalCell, StaticRef};
10use kernel::ErrorCode;
11
12register_structs! {
13    /// Control and data interface to SAR ADC
14    AdcRegisters {
15        /// ADC Control and Status
16        (0x000 => cs: ReadWrite<u32, CS::Register>),
17        /// Result of most recent ADC conversion
18        (0x004 => result: ReadWrite<u32, RESULT::Register>),
19        /// FIFO control and status
20        (0x008 => fcs: ReadWrite<u32, FCS::Register>),
21        /// Conversion result FIFO
22        (0x00C => fifo: ReadWrite<u32, FIFO::Register>),
23        /// Clock divider. If non-zero, CS_START_MANY will start conversions
24        /// at regular intervals rather than back-to-back.
25        /// The divider is reset when either of these fields are written.
26        /// Total period is 1 + INT + FRAC / 256
27        (0x010 => div: ReadWrite<u32, DIV::Register>),
28        /// Raw Interrupts
29        (0x014 => intr: ReadWrite<u32, INTR::Register>),
30        /// Interrupt Enable
31        (0x018 => inte: ReadWrite<u32, INTE::Register>),
32        /// Interrupt Force
33        (0x01C => intf: ReadWrite<u32, INTE::Register>),
34        /// Interrupt status after masking & forcing
35        (0x020 => ints: ReadWrite<u32, INTE::Register>),
36        (0x024 => @END),
37    }
38}
39register_bitfields![u32,
40CS [
41    /// Round-robin sampling. 1 bit per channel. Set all bits to 0 to disable.
42    /// Otherwise, the ADC will cycle through each enabled channel in a
43    /// The first channel to be sampled will be the one currently indica
44    /// AINSEL will be updated after each conversion with the newly-sele
45    RROBIN OFFSET(16) NUMBITS(5) [],
46    /// Select analog mux input. Updated automatically in round-robin mode.
47    AINSEL OFFSET(12) NUMBITS(3) [],
48    /// Some past ADC conversion encountered an error. Write 1 to clear.
49    ERR_STICKY OFFSET(10) NUMBITS(1) [],
50    /// The most recent ADC conversion encountered an error; result is undefined or nois
51    ERR OFFSET(9) NUMBITS(1) [],
52    /// 1 if the ADC is ready to start a new conversion. Implies any previous conversion
53    /// 0 whilst conversion in progress.
54    READY OFFSET(8) NUMBITS(1) [],
55    /// Continuously perform conversions whilst this bit is 1. A new conversion will sta
56    START_MANY OFFSET(3) NUMBITS(1) [],
57    /// Start a single conversion. Self-clearing. Ignored if start_many is asserted.
58    START_ONCE OFFSET(2) NUMBITS(1) [],
59    /// Power on temperature sensor. 1 - enabled. 0 - disabled.
60    TS_EN OFFSET(1) NUMBITS(1) [],
61    /// Power on ADC and enable its clock.
62    /// 1 - enabled. 0 - disabled.
63    EN OFFSET(0) NUMBITS(1) []
64],
65RESULT [
66
67    RESULT OFFSET(0) NUMBITS(12) []
68],
69FCS [
70    /// DREQ/IRQ asserted when level >= threshold
71    THRESH OFFSET(24) NUMBITS(4) [],
72    /// The number of conversion results currently waiting in the FIFO
73    LEVEL OFFSET(16) NUMBITS(4) [],
74    /// 1 if the FIFO has been overflowed. Write 1 to clear.
75    OVER OFFSET(11) NUMBITS(1) [],
76    /// 1 if the FIFO has been underflowed. Write 1 to clear.
77    UNDER OFFSET(10) NUMBITS(1) [],
78
79    FULL OFFSET(9) NUMBITS(1) [],
80
81    EMPTY OFFSET(8) NUMBITS(1) [],
82    /// If 1: assert DMA requests when FIFO contains data
83    DREQ_EN OFFSET(3) NUMBITS(1) [],
84    /// If 1: conversion error bit appears in the FIFO alongside the result
85    ERR OFFSET(2) NUMBITS(1) [],
86    /// If 1: FIFO results are right-shifted to be one byte in size. Enables DMA to byte
87    SHIFT OFFSET(1) NUMBITS(1) [],
88    /// If 1: write result to the FIFO after each conversion.
89    EN OFFSET(0) NUMBITS(1) []
90],
91FIFO [
92    /// 1 if this particular sample experienced a conversion error. Remains in the same
93    ERR OFFSET(15) NUMBITS(1) [],
94
95    VAL OFFSET(0) NUMBITS(12) []
96],
97DIV [
98    /// Integer part of clock divisor.
99    INT OFFSET(8) NUMBITS(16) [],
100    /// Fractional part of clock divisor. First-order delta-sigma.
101    FRAC OFFSET(0) NUMBITS(8) []
102],
103INTR [
104    /// Triggered when the sample FIFO reaches a certain level.
105    /// This level can be programmed via the FCS_THRESH field.
106    FIFO OFFSET(0) NUMBITS(1) []
107],
108INTE [
109    /// Triggered when the sample FIFO reaches a certain level.
110    /// This level can be programmed via the FCS_THRESH field.
111    FIFO OFFSET(0) NUMBITS(1) []
112],
113INTF [
114    /// Triggered when the sample FIFO reaches a certain level.
115    /// This level can be programmed via the FCS_THRESH field.
116    FIFO OFFSET(0) NUMBITS(1) []
117],
118INTS [
119    /// Triggered when the sample FIFO reaches a certain level.
120    /// This level can be programmed via the FCS_THRESH field.
121    FIFO OFFSET(0) NUMBITS(1) []
122]
123];
124const ADC_BASE: StaticRef<AdcRegisters> =
125    unsafe { StaticRef::new(0x4004C000 as *const AdcRegisters) };
126
127#[allow(dead_code)]
128#[repr(u32)]
129#[derive(Copy, Clone, PartialEq)]
130pub enum Channel {
131    Channel0 = 0b00000,
132    Channel1 = 0b00001,
133    Channel2 = 0b00010,
134    Channel3 = 0b00011,
135    Channel4 = 0b00100,
136}
137
138#[derive(Copy, Clone, PartialEq)]
139enum ADCStatus {
140    Idle,
141    OneSample,
142}
143
144pub struct Adc<'a> {
145    registers: StaticRef<AdcRegisters>,
146    status: Cell<ADCStatus>,
147    channel: Cell<Channel>,
148    client: OptionalCell<&'a dyn hil::adc::Client>,
149}
150
151impl Adc<'_> {
152    pub const fn new() -> Self {
153        Self {
154            registers: ADC_BASE,
155            status: Cell::new(ADCStatus::Idle),
156            channel: Cell::new(Channel::Channel0),
157            client: OptionalCell::empty(),
158        }
159    }
160
161    pub fn init(&self) {
162        self.registers.cs.modify(CS::EN::SET);
163        while !self.registers.cs.is_set(CS::READY) {}
164    }
165
166    pub fn disable(&self) {
167        self.registers.cs.modify(CS::EN::CLEAR);
168    }
169
170    fn enable_interrupt(&self) {
171        self.registers.inte.modify(INTE::FIFO::SET);
172    }
173
174    fn disable_interrupt(&self) {
175        self.registers.inte.modify(INTE::FIFO::CLEAR);
176    }
177
178    fn enable_temperature(&self) {
179        self.registers.cs.modify(CS::TS_EN::SET);
180    }
181
182    pub fn handle_interrupt(&self) {
183        if self.registers.cs.is_set(CS::READY) {
184            if self.status.get() == ADCStatus::OneSample {
185                self.status.set(ADCStatus::Idle);
186            }
187            self.client.map(|client| {
188                self.disable_interrupt();
189                client.sample_ready((self.registers.fifo.read(FIFO::VAL) << 4) as u16)
190            });
191        }
192    }
193}
194
195impl<'a> hil::adc::Adc<'a> for Adc<'a> {
196    type Channel = Channel;
197
198    fn sample(&self, channel: &Self::Channel) -> Result<(), ErrorCode> {
199        if self.status.get() == ADCStatus::Idle {
200            if *channel as u32 == 4 {
201                self.enable_temperature();
202            }
203            self.status.set(ADCStatus::OneSample);
204            self.channel.set(*channel);
205            self.registers.cs.modify(CS::AINSEL.val(*channel as u32));
206            self.registers
207                .fcs
208                .modify(FCS::THRESH.val(1_u32) + FCS::EN::SET);
209            self.enable_interrupt();
210            self.registers.cs.modify(CS::START_ONCE::SET);
211            Ok(())
212        } else {
213            Err(ErrorCode::BUSY)
214        }
215    }
216
217    fn sample_continuous(
218        &self,
219        _channel: &Self::Channel,
220        _frequency: u32,
221    ) -> Result<(), ErrorCode> {
222        Err(ErrorCode::NOSUPPORT)
223    }
224
225    fn stop_sampling(&self) -> Result<(), ErrorCode> {
226        Err(ErrorCode::NOSUPPORT)
227    }
228
229    fn get_resolution_bits(&self) -> usize {
230        12
231    }
232
233    fn get_voltage_reference_mv(&self) -> Option<usize> {
234        Some(3300)
235    }
236
237    fn set_client(&self, client: &'a dyn hil::adc::Client) {
238        self.client.set(client);
239    }
240}