From 17e65c4fd5101bc9cc93034aedb7b950b24751fb Mon Sep 17 00:00:00 2001 From: Hmvp Date: Wed, 7 Jul 2021 15:38:25 +0200 Subject: [PATCH] Initial ADC impl (#52) * Initial ADC + temp sensor support --- rp2040-hal/src/adc.rs | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 905455b..dac3054 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -1,3 +1,118 @@ //! Analog-Digital Converter (ADC) // See [Chapter 4 Section 9](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details // TODO + +use hal::adc::{Channel, OneShot}; +use pac::{ADC, RESETS}; + +use crate::{ + gpio::Pin, + gpio::{ + bank0::{Gpio26, Gpio27, Gpio28, Gpio29}, + FloatingInput, + }, + resets::SubsystemReset, +}; + +const TEMPERATURE_SENSOR_CHANNEL: u8 = 4; + +/// Adc +pub struct Adc { + device: ADC, +} + +impl Adc { + /// Create new adc struct and bring up adc + pub fn new(device: ADC, resets: &mut RESETS) -> Self { + device.reset_bring_down(resets); + device.reset_bring_up(resets); + + // Enable adc + device.cs.write(|w| w.en().set_bit()); + + // Wait for adc ready + while !device.cs.read().ready().bit_is_set() {} + + Self { device } + } + + /// Free underlying register block + pub fn free(self) -> ADC { + self.device + } + + /// Read single + pub fn read_single(&self) -> u16 { + self.device.result.read().result().bits() + } + + /// Enable temperature sensor, returns a channel to use + pub fn enable_temp_sensor(&mut self) -> TempSense { + self.device.cs.modify(|_, w| w.ts_en().set_bit()); + + TempSense { __private: () } + } + + /// Disable temperature sensor, consumes channel + pub fn disable_temp_sensor(&mut self, _: TempSense) { + self.device.cs.modify(|_, w| w.ts_en().clear_bit()); + } +} + +macro_rules! channel { + ($pin:ident, $channel:expr) => { + impl Channel for Pin<$pin, FloatingInput> { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + $channel + } + } + }; +} + +channel!(Gpio26, 0); +channel!(Gpio27, 1); +channel!(Gpio28, 2); +channel!(Gpio29, 3); + +/// Internal temperature sensor type +pub struct TempSense { + __private: (), +} + +impl Channel for TempSense { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + TEMPERATURE_SENSOR_CHANNEL + } +} + +impl OneShot for Adc +where + WORD: From, + PIN: Channel, +{ + type Error = (); + + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + let chan = PIN::channel(); + + if chan == 4 { + self.device.cs.modify(|_, w| w.ts_en().set_bit()) + } + + if self.device.cs.read().ready().bit_is_set() { + self.device + .cs + .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + }; + if !self.device.cs.read().ready().bit_is_set() { + // Can't return WouldBlock here since that would take to long and next call conversion would be over + cortex_m::asm::nop(); + }; + + Ok(self.device.result.read().result().bits().into()) + } +}