aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/adc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/adc.rs')
-rw-r--r--embassy-mcxa/src/adc.rs409
1 files changed, 409 insertions, 0 deletions
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs
new file mode 100644
index 000000000..b5ec5983f
--- /dev/null
+++ b/embassy-mcxa/src/adc.rs
@@ -0,0 +1,409 @@
1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
7use crate::clocks::{enable_and_reset, Gate, PoweredClock};
8use crate::pac;
9use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
10use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
11use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode};
12use crate::pac::adc1::ctrl::CalAvgs;
13use crate::pac::adc1::tctrl::{Tcmd, Tpri};
14
15type Regs = pac::adc1::RegisterBlock;
16
17static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
18// Token-based instance pattern like embassy-imxrt
19pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType {
20 fn ptr() -> *const Regs;
21}
22
23/// Token for ADC1
24pub type Adc1 = crate::peripherals::ADC1;
25impl Instance for crate::peripherals::ADC1 {
26 #[inline(always)]
27 fn ptr() -> *const Regs {
28 pac::Adc1::ptr()
29 }
30}
31
32// Also implement Instance for the Peri wrapper type
33// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
34// #[inline(always)]
35// fn ptr() -> *const Regs {
36// pac::Adc1::ptr()
37// }
38// }
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(u8)]
42pub enum TriggerPriorityPolicy {
43 ConvPreemptImmediatelyNotAutoResumed = 0,
44 ConvPreemptSoftlyNotAutoResumed = 1,
45 ConvPreemptImmediatelyAutoRestarted = 4,
46 ConvPreemptSoftlyAutoRestarted = 5,
47 ConvPreemptImmediatelyAutoResumed = 12,
48 ConvPreemptSoftlyAutoResumed = 13,
49 ConvPreemptSubsequentlyNotAutoResumed = 2,
50 ConvPreemptSubsequentlyAutoRestarted = 6,
51 ConvPreemptSubsequentlyAutoResumed = 14,
52 TriggerPriorityExceptionDisabled = 16,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct LpadcConfig {
57 pub enable_in_doze_mode: bool,
58 pub conversion_average_mode: CalAvgs,
59 pub enable_analog_preliminary: bool,
60 pub power_up_delay: u8,
61 pub reference_voltage_source: Refsel,
62 pub power_level_mode: Pwrsel,
63 pub trigger_priority_policy: TriggerPriorityPolicy,
64 pub enable_conv_pause: bool,
65 pub conv_pause_delay: u16,
66 pub fifo_watermark: u8,
67 pub power: PoweredClock,
68 pub source: AdcClockSel,
69 pub div: Div4,
70}
71
72impl Default for LpadcConfig {
73 fn default() -> Self {
74 LpadcConfig {
75 enable_in_doze_mode: true,
76 conversion_average_mode: CalAvgs::NoAverage,
77 enable_analog_preliminary: false,
78 power_up_delay: 0x80,
79 reference_voltage_source: Refsel::Option1,
80 power_level_mode: Pwrsel::Lowest,
81 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
82 enable_conv_pause: false,
83 conv_pause_delay: 0,
84 fifo_watermark: 0,
85 power: PoweredClock::NormalEnabledDeepSleepDisabled,
86 source: AdcClockSel::FroLfDiv,
87 div: Div4::no_div(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub struct ConvCommandConfig {
94 pub sample_channel_mode: Ctype,
95 pub channel_number: Adch,
96 pub chained_next_command_number: Next,
97 pub enable_auto_channel_increment: bool,
98 pub loop_count: u8,
99 pub hardware_average_mode: Avgs,
100 pub sample_time_mode: Sts,
101 pub hardware_compare_mode: Cmpen,
102 pub hardware_compare_value_high: u32,
103 pub hardware_compare_value_low: u32,
104 pub conversion_resolution_mode: Mode,
105 pub enable_wait_trigger: bool,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct ConvTriggerConfig {
110 pub target_command_id: Tcmd,
111 pub delay_power: u8,
112 pub priority: Tpri,
113 pub enable_hardware_trigger: bool,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct ConvResult {
118 pub command_id_source: u32,
119 pub loop_count_index: u32,
120 pub trigger_id_source: u32,
121 pub conv_value: u16,
122}
123
124pub struct Adc<'a, I: Instance> {
125 _inst: core::marker::PhantomData<&'a mut I>,
126}
127
128impl<'a, I: Instance> Adc<'a, I> {
129 /// initialize ADC
130 pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self {
131 let adc = unsafe { &*I::ptr() };
132
133 let _clock_freq = unsafe {
134 enable_and_reset::<I>(&AdcConfig {
135 power: config.power,
136 source: config.source,
137 div: config.div,
138 })
139 .expect("Adc Init should not fail")
140 };
141
142 /* Reset the module. */
143 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
144 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
145
146 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
147
148 /* Disable the module before setting configuration. */
149 adc.ctrl().modify(|_, w| w.adcen().disabled());
150
151 /* Configure the module generally. */
152 if config.enable_in_doze_mode {
153 adc.ctrl().modify(|_, w| w.dozen().enabled());
154 } else {
155 adc.ctrl().modify(|_, w| w.dozen().disabled());
156 }
157
158 /* Set calibration average mode. */
159 adc.ctrl()
160 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
161
162 adc.cfg().write(|w| unsafe {
163 let w = if config.enable_analog_preliminary {
164 w.pwren().pre_enabled()
165 } else {
166 w
167 };
168
169 w.pudly()
170 .bits(config.power_up_delay)
171 .refsel()
172 .variant(config.reference_voltage_source)
173 .pwrsel()
174 .variant(config.power_level_mode)
175 .tprictrl()
176 .variant(match config.trigger_priority_policy {
177 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
178 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
179 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
180 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
181 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
182 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
183 _ => Tprictrl::AbortCurrentOnPriority,
184 })
185 .tres()
186 .variant(match config.trigger_priority_policy {
187 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
188 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
189 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
190 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
191 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
192 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
193 _ => Tres::Disabled,
194 })
195 .tcmdres()
196 .variant(match config.trigger_priority_policy {
197 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
198 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
199 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
200 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
201 _ => Tcmdres::Disabled,
202 })
203 .hpt_exdi()
204 .variant(match config.trigger_priority_policy {
205 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
206 _ => HptExdi::Enabled,
207 })
208 });
209
210 if config.enable_conv_pause {
211 adc.pause()
212 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
213 } else {
214 adc.pause().write(|w| unsafe { w.bits(0) });
215 }
216
217 adc.fctrl0()
218 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
219
220 // Enable ADC
221 adc.ctrl().modify(|_, w| w.adcen().enabled());
222
223 Self {
224 _inst: core::marker::PhantomData,
225 }
226 }
227
228 pub fn deinit(&self) {
229 let adc = unsafe { &*I::ptr() };
230 adc.ctrl().modify(|_, w| w.adcen().disabled());
231 }
232
233 pub fn do_offset_calibration(&self) {
234 let adc = unsafe { &*I::ptr() };
235 // Enable calibration mode
236 adc.ctrl()
237 .modify(|_, w| w.calofs().offset_calibration_request_pending());
238
239 // Wait for calibration to complete (polling status register)
240 while adc.stat().read().cal_rdy().is_not_set() {}
241 }
242
243 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
244 let mut gcra_array = [0u32; 17];
245 let mut gcalr: u32 = 0;
246
247 for i in (1..=17).rev() {
248 let shift = 16 - (i - 1);
249 let step = 1.0 / (1u32 << shift) as f32;
250 let tmp = (gain_adjustment / step) as u32;
251 gcra_array[i - 1] = tmp;
252 gain_adjustment -= tmp as f32 * step;
253 }
254
255 for i in (1..=17).rev() {
256 gcalr += gcra_array[i - 1] << (i - 1);
257 }
258 gcalr
259 }
260
261 pub fn do_auto_calibration(&self) {
262 let adc = unsafe { &*I::ptr() };
263 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
264
265 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
266
267 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
268 if gcca & ((0xFFFF + 1) >> 1) != 0 {
269 gcca |= !0xFFFF;
270 }
271
272 let gcra = 131072.0 / (131072.0 - gcca as f32);
273
274 // Write to GCR0
275 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
276
277 adc.gcr0().modify(|_, w| w.rdy().set_bit());
278
279 // Wait for calibration to complete (polling status register)
280 while adc.stat().read().cal_rdy().is_not_set() {}
281 }
282
283 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
284 let adc = unsafe { &*I::ptr() };
285 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
286 }
287
288 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
289 ConvCommandConfig {
290 sample_channel_mode: Ctype::SingleEndedASideChannel,
291 channel_number: Adch::SelectCh0,
292 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
293 enable_auto_channel_increment: false,
294 loop_count: 0,
295 hardware_average_mode: Avgs::NoAverage,
296 sample_time_mode: Sts::Sample3p5,
297 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
298 hardware_compare_value_high: 0,
299 hardware_compare_value_low: 0,
300 conversion_resolution_mode: Mode::Data12Bits,
301 enable_wait_trigger: false,
302 }
303 }
304
305 //TBD Need to add cmdlx and cmdhx with x {2..7}
306 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) {
307 let adc = unsafe { &*I::ptr() };
308
309 match index {
310 1 => {
311 adc.cmdl1().write(|w| {
312 w.adch()
313 .variant(config.channel_number)
314 .mode()
315 .variant(config.conversion_resolution_mode)
316 });
317 adc.cmdh1().write(|w| unsafe {
318 w.next()
319 .variant(config.chained_next_command_number)
320 .loop_()
321 .bits(config.loop_count)
322 .avgs()
323 .variant(config.hardware_average_mode)
324 .sts()
325 .variant(config.sample_time_mode)
326 .cmpen()
327 .variant(config.hardware_compare_mode);
328 if config.enable_wait_trigger {
329 w.wait_trig().enabled();
330 }
331 if config.enable_auto_channel_increment {
332 w.lwi().enabled();
333 }
334 w
335 });
336 }
337 _ => panic!("Invalid command index: must be between 1 and 7"),
338 }
339 }
340
341 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
342 ConvTriggerConfig {
343 target_command_id: Tcmd::NotValid,
344 delay_power: 0,
345 priority: Tpri::HighestPriority,
346 enable_hardware_trigger: false,
347 }
348 }
349
350 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
351 let adc = unsafe { &*I::ptr() };
352 let tctrl = &adc.tctrl(trigger_id);
353
354 tctrl.write(|w| unsafe {
355 let w = w.tcmd().variant(config.target_command_id);
356 let w = w.tdly().bits(config.delay_power);
357 w.tpri().variant(config.priority);
358 if config.enable_hardware_trigger {
359 w.hten().enabled()
360 } else {
361 w
362 }
363 });
364 }
365
366 pub fn do_reset_fifo(&self) {
367 let adc = unsafe { &*I::ptr() };
368 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
369 }
370
371 pub fn enable_interrupt(&self, mask: u32) {
372 let adc = unsafe { &*I::ptr() };
373 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
374 INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst);
375 }
376
377 pub fn is_interrupt_triggered(&self) -> bool {
378 INTERRUPT_TRIGGERED.load(Ordering::Relaxed)
379 }
380}
381
382pub fn get_conv_result() -> Option<ConvResult> {
383 let adc = unsafe { &*pac::Adc1::ptr() };
384 let fifo = adc.resfifo0().read().bits();
385 const VALID_MASK: u32 = 1 << 31;
386 if fifo & VALID_MASK == 0 {
387 return None;
388 }
389
390 Some(ConvResult {
391 command_id_source: (fifo >> 24) & 0x0F,
392 loop_count_index: (fifo >> 20) & 0x0F,
393 trigger_id_source: (fifo >> 16) & 0x0F,
394 conv_value: (fifo & 0xFFFF) as u16,
395 })
396}
397
398pub fn on_interrupt() {
399 if get_conv_result().is_some() {
400 INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst);
401 }
402}
403
404pub struct AdcHandler;
405impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler {
406 unsafe fn on_interrupt() {
407 on_interrupt();
408 }
409}