avr_hal_generic/adc.rs
1/// Analog-to-Digial converter
2use core::marker::PhantomData;
3
4/// The division factor between the system clock frequency and the input clock to the AD converter.
5///
6/// To get 10-bit precision, clock from 50kHz to 200kHz must be supplied. If you need less
7/// precision, you can supply a higher clock.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum ClockDivider {
11 Factor2,
12 Factor4,
13 Factor8,
14 Factor16,
15 Factor32,
16 Factor64,
17 /// (default)
18 Factor128,
19}
20
21impl Default for ClockDivider {
22 fn default() -> Self {
23 Self::Factor128
24 }
25}
26
27/// Internal trait for the low-level ADC peripheral.
28///
29/// **Prefer using the [`Adc`] API instead of this trait.**
30pub trait AdcOps<H> {
31 /// Channel ID type for this ADC.
32 type Channel: PartialEq + Copy;
33
34 /// Settings type for this ADC.
35 type Settings: PartialEq + Copy;
36
37 /// Initialize the ADC peripheral with the specified settings.
38 ///
39 /// **Warning**: This is a low-level method and should not be called directly from user code.
40 fn raw_init(&mut self, settings: Self::Settings);
41
42 /// Read out the ADC data register.
43 ///
44 /// This method must only be called after a conversion completed.
45 ///
46 /// **Warning**: This is a low-level method and should not be called directly from user code.
47 fn raw_read_adc(&self) -> u16;
48
49 /// Check whether the ADC is currently converting a signal.
50 ///
51 /// **Warning**: This is a low-level method and should not be called directly from user code.
52 fn raw_is_converting(&self) -> bool;
53
54 /// Start a conversion on the currently selected channel.
55 ///
56 /// **Warning**: This is a low-level method and should not be called directly from user code.
57 fn raw_start_conversion(&mut self);
58
59 /// Set the multiplexer to a certain channel.
60 ///
61 /// **Warning**: This is a low-level method and should not be called directly from user code.
62 fn raw_set_channel(&mut self, channel: Self::Channel);
63
64 /// Set the DIDR (Digital Input Disable) for a certain channel.
65 ///
66 /// This disabled digital logic on the corresponding pin and allows measuring analog signals.
67 ///
68 /// **Warning**: This is a low-level method and should not be called directly from user code.
69 fn raw_enable_channel(&mut self, channel: Self::Channel);
70
71 /// Clear the DIDR (Digital Input Disable) for a certain channel.
72 ///
73 /// Enables digital logic on the corresponding pin after it has been used as an ADC channel.
74 ///
75 /// **Warning**: This is a low-level method and should not be called directly from user code.
76 fn raw_disable_channel(&mut self, channel: Self::Channel);
77}
78
79/// Trait marking a type as an ADC channel for a certain ADC.
80pub trait AdcChannel<H, ADC: AdcOps<H>> {
81 fn channel(&self) -> ADC::Channel;
82}
83
84/// Representation of any ADC Channel.
85///
86/// Typically, distinct types are used per channel, like for example `Pin<mode::Analog, PC0>`. In
87/// some situations, however, a type is needed which can represent _any_ channel. This is required
88/// to, for example, store multiple channels in an array.
89///
90/// `Channel` is such a type. It can be created by calling the [`into_channel()`][into-channel]
91/// method of a distinct type:
92///
93/// ```
94/// let a0 = pins.a0.into_analog_input(&mut adc);
95/// let a1 = pins.a1.into_analog_input(&mut adc);
96///
97/// let channels: [atmega_hal::adc::Channel; 2] = [
98/// a0.into_channel(),
99/// a1.into_channel(),
100/// ];
101///
102/// for ch in channels.iter() {
103/// adc.read_blocking(ch);
104/// }
105/// ```
106///
107/// [into-channel]: crate::port::Pin::into_channel
108pub struct Channel<H, ADC: AdcOps<H>> {
109 ch: ADC::Channel,
110 _h: PhantomData<H>,
111}
112
113impl<H, ADC: AdcOps<H>> Channel<H, ADC> {
114 pub fn new<CH: AdcChannel<H, ADC>>(ch: CH) -> Self {
115 Self {
116 ch: ch.channel(),
117 _h: PhantomData,
118 }
119 }
120}
121
122impl<H, ADC: AdcOps<H>> AdcChannel<H, ADC> for Channel<H, ADC> {
123 #[inline]
124 fn channel(&self) -> ADC::Channel {
125 self.ch
126 }
127}
128
129/// Analog-to-Digital Converter
130/// ```
131/// let dp = atmega_hal::Peripherals::take().unwrap();
132/// let pins = atmega_hal::pins!(dp);
133/// let mut adc = atmega_hal::Adc::new(dp.ADC, Default::default());
134///
135/// let a0 = pins.pc0.into_analog_input(&mut adc);
136///
137/// // the following two calls are equivalent
138/// let voltage = a0.analog_read(&mut adc);
139/// let voltage = adc.read_blocking(&a0);
140///
141/// // alternatively, a non-blocking interface exists
142/// let voltage = nb::block!(adc.read_nonblocking(&a0)).unwrap_infallible();
143/// ```
144pub struct Adc<H, ADC: AdcOps<H>, CLOCK> {
145 p: ADC,
146 reading_channel: Option<ADC::Channel>,
147 _clock: PhantomData<CLOCK>,
148 _h: PhantomData<H>,
149}
150
151impl<H, ADC, CLOCK> Adc<H, ADC, CLOCK>
152where
153 ADC: AdcOps<H>,
154 CLOCK: crate::clock::Clock,
155{
156 pub fn new(p: ADC, settings: ADC::Settings) -> Self {
157 let mut adc = Self {
158 p,
159 reading_channel: None,
160 _clock: PhantomData,
161 _h: PhantomData,
162 };
163 adc.initialize(settings);
164 adc
165 }
166
167 pub fn initialize(&mut self, settings: ADC::Settings) {
168 self.p.raw_init(settings);
169 }
170
171 #[inline]
172 pub(crate) fn enable_pin<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) {
173 self.p.raw_enable_channel(pin.channel());
174 }
175
176 #[inline]
177 pub(crate) fn disable_pin<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) {
178 self.p.raw_disable_channel(pin.channel());
179 }
180
181 pub fn read_blocking<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) -> u16 {
182 // assert!(self.reading_channel.is_none());
183 self.p.raw_set_channel(pin.channel());
184 self.p.raw_start_conversion();
185 while self.p.raw_is_converting() {}
186 self.p.raw_read_adc()
187 }
188
189 pub fn read_nonblocking<PIN: AdcChannel<H, ADC>>(
190 &mut self,
191 pin: &PIN,
192 ) -> nb::Result<u16, core::convert::Infallible> {
193 match (&self.reading_channel, self.p.raw_is_converting()) {
194 // Measurement on current pin is ongoing
195 (Some(channel), true) if *channel == pin.channel() => Err(nb::Error::WouldBlock),
196 // Measurement on current pin completed
197 (Some(channel), false) if *channel == pin.channel() => {
198 self.reading_channel = None;
199 Ok(self.p.raw_read_adc())
200 }
201 // Measurement on other pin is ongoing
202 (Some(_), _) => {
203 self.reading_channel = None;
204 Err(nb::Error::WouldBlock)
205 }
206 // Start measurement
207 (None, _) => {
208 self.reading_channel = Some(pin.channel());
209 self.p.raw_set_channel(pin.channel());
210 self.p.raw_start_conversion();
211 Err(nb::Error::WouldBlock)
212 }
213 }
214 }
215}
216
217#[macro_export]
218macro_rules! impl_adc {
219 (
220 hal: $HAL:ty,
221 peripheral: $ADC:ty,
222 settings: $Settings:ty,
223 apply_settings: |$settings_periph_var:ident, $settings_var:ident| $apply_settings:block,
224 channel_id: $Channel:ty,
225 set_channel: |$periph_var:ident, $chan_var:ident| $set_channel:block,
226 pins: {
227 $(
228 $(#[$pin_attr:meta])*
229 $pin:ty: ($pin_channel:expr$(, $didr:ident::$didr_method:ident)?),
230 )+
231 },
232 $(channels: {
233 $(
234 $(#[$channel_attr:meta])*
235 $channel_ty:ty: $channel:expr,
236 )*
237 },)?
238 ) => {
239 impl $crate::adc::AdcOps<$HAL> for $ADC {
240 type Channel = $Channel;
241 type Settings = $Settings;
242
243 #[inline]
244 fn raw_init(&mut self, settings: Self::Settings) {
245 let $settings_periph_var = self;
246 let $settings_var = settings;
247
248 $apply_settings
249 }
250
251 #[inline]
252 fn raw_read_adc(&self) -> u16 {
253 self.adc.read().bits()
254 }
255
256 #[inline]
257 fn raw_is_converting(&self) -> bool {
258 self.adcsra.read().adsc().bit_is_set()
259 }
260
261 #[inline]
262 fn raw_start_conversion(&mut self) {
263 self.adcsra.modify(|_, w| w.adsc().set_bit());
264 }
265
266 #[inline]
267 fn raw_set_channel(&mut self, channel: Self::Channel) {
268 let $periph_var = self;
269 let $chan_var = channel;
270
271 $set_channel
272 }
273
274 #[inline]
275 fn raw_enable_channel(&mut self, channel: Self::Channel) {
276 match channel {
277 $(
278 x if x == $pin_channel => {
279 $(self.$didr.modify(|_, w| w.$didr_method().set_bit());)?
280 }
281 )+
282 _ => unreachable!(),
283 }
284 }
285
286 #[inline]
287 fn raw_disable_channel(&mut self, channel: Self::Channel) {
288 match channel {
289 $(
290 x if x == $pin_channel => {
291 $(self.$didr.modify(|_, w| w.$didr_method().clear_bit());)?
292 }
293 )+
294 _ => unreachable!(),
295 }
296 }
297 }
298
299 $(
300 $(#[$pin_attr])*
301 impl $crate::adc::AdcChannel<$HAL, $ADC> for $crate::port::Pin<$crate::port::mode::Analog, $pin> {
302 #[inline]
303 fn channel(&self) -> $Channel {
304 $pin_channel
305 }
306 }
307 )+
308
309 $($(
310 $(#[$channel_attr])*
311 impl $crate::adc::AdcChannel<$HAL, $ADC> for $channel_ty {
312 #[inline]
313 fn channel(&self) -> $Channel {
314 $channel
315 }
316 }
317
318 /// Convert this channel into a generic "[`Channel`][adc-channel]" type.
319 ///
320 /// The generic channel type can be used to store multiple channels in an array.
321 ///
322 /// [adc-channel]: crate::adc::Channel
323 $(#[$channel_attr])*
324 impl $channel_ty {
325 pub fn into_channel(self) -> $crate::adc::Channel<$HAL, $ADC> {
326 $crate::adc::Channel::new(self)
327 }
328 }
329 )*)?
330 };
331}