diff --git a/esp-hal-common/.vscode/settings.json b/esp-hal-common/.vscode/settings.json index 293965674..3864e3c46 100644 --- a/esp-hal-common/.vscode/settings.json +++ b/esp-hal-common/.vscode/settings.json @@ -1,21 +1,21 @@ { "rust-analyzer.cargo.features": [ - "esp32s2" + "esp32c3" ], "rust-analyzer.cargo.allFeatures": false, "editor.formatOnSave": true, - "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.allTargets": true, "rust-analyzer.checkOnSave.allFeatures": false, "rust-analyzer.checkOnSave.overrideCommand": [ "cargo", "check", "--features", - "esp32s2", + "esp32c3", "--message-format=json", "-Z", "build-std=core", "--target", - "xtensa-esp32s2-none-elf", + "riscv32imc-unknown-none-elf", "--examples", "--lib", ], diff --git a/esp-hal-common/src/analog/adc/esp32.rs b/esp-hal-common/src/analog/adc/esp32.rs index 343482b41..55afbb1ef 100644 --- a/esp-hal-common/src/analog/adc/esp32.rs +++ b/esp-hal-common/src/analog/adc/esp32.rs @@ -82,6 +82,7 @@ pub trait RegisterAccess { fn read_data_sar() -> u16; } +#[doc(hidden)] impl RegisterAccess for ADC1 { fn set_bit_width(resolution: u8) { let sensors = unsafe { &*SENS::ptr() }; @@ -375,6 +376,7 @@ where } } +#[doc(hidden)] #[macro_export] macro_rules! impl_adc_interface { ($adc:ident [ diff --git a/esp-hal-common/src/analog/adc/esp32c3.rs b/esp-hal-common/src/analog/adc/esp32c3.rs index 8cf7e7c44..156b6c938 100644 --- a/esp-hal-common/src/analog/adc/esp32c3.rs +++ b/esp-hal-common/src/analog/adc/esp32c3.rs @@ -1 +1,263 @@ -// Currently we are missing the SENS peripheral from the SVD +use core::marker::PhantomData; + +use embedded_hal::adc::{Channel, OneShot}; + +use crate::{ + analog::{ADC1, ADC2}, + pac::APB_SARADC, + system::{Peripheral, PeripheralClockControl}, +}; + +/// The sampling/readout resolution of the ADC +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Resolution { + Resolution12Bit, +} + +/// The attenuation of the ADC pin +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Attenuation { + Attenuation0dB = 0b00, + Attenuation2p5dB = 0b01, + Attenuation6dB = 0b10, + Attenuation11dB = 0b11, +} + +pub struct AdcConfig { + pub resolution: Resolution, + pub attenuations: [Option; 5], + _phantom: PhantomData, +} + +impl AdcConfig +where + ADCI: RegisterAccess, +{ + pub fn new() -> AdcConfig { + Self::default() + } + + pub fn enable_pin>( + &mut self, + _pin: &PIN, + attenuation: Attenuation, + ) { + self.attenuations[PIN::channel() as usize] = Some(attenuation); + } +} + +impl Default for AdcConfig { + fn default() -> Self { + AdcConfig { + resolution: Resolution::Resolution12Bit, + attenuations: [None; 5], + _phantom: PhantomData::default(), + } + } +} + +#[doc(hidden)] +pub trait RegisterAccess { + fn start_onetime_sample(channel: u8, attenuation: u8); + + fn is_done() -> bool; + + fn read_data() -> u16; + + fn reset(); +} + +impl RegisterAccess for ADC1 { + fn start_onetime_sample(channel: u8, attenuation: u8) { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc.onetime_sample.modify(|_, w| unsafe { + w.saradc1_onetime_sample() + .set_bit() + .saradc_onetime_channel() + .bits(channel) + .saradc_onetime_atten() + .bits(attenuation) + .saradc_onetime_start() + .set_bit() + }); + } + + fn is_done() -> bool { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc.int_raw.read().apb_saradc1_done_int_raw().bit() + } + + fn read_data() -> u16 { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + (sar_adc.sar1data_status.read().apb_saradc1_data().bits() as u16) & 0xfff + } + + fn reset() { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc + .int_clr + .write(|w| w.apb_saradc1_done_int_clr().set_bit()); + + sar_adc + .onetime_sample + .modify(|_, w| w.saradc_onetime_start().clear_bit()); + } +} + +impl RegisterAccess for ADC2 { + fn start_onetime_sample(channel: u8, attenuation: u8) { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc.onetime_sample.modify(|_, w| unsafe { + w.saradc2_onetime_sample() + .set_bit() + .saradc_onetime_channel() + .bits(channel) + .saradc_onetime_atten() + .bits(attenuation) + .saradc_onetime_start() + .set_bit() + }); + } + + fn is_done() -> bool { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc.int_raw.read().apb_saradc2_done_int_raw().bit() + } + + fn read_data() -> u16 { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + (sar_adc.sar2data_status.read().apb_saradc2_data().bits() as u16) & 0xfff + } + + fn reset() { + let sar_adc = unsafe { &*APB_SARADC::PTR }; + + sar_adc + .int_clr + .write(|w| w.apb_saradc2_done_int_clr().set_bit()); + + sar_adc + .onetime_sample + .modify(|_, w| w.saradc_onetime_start().clear_bit()); + } +} + +pub struct ADC { + adc: PhantomData, + attenuations: [Option; 5], + active_channel: Option, +} + +impl ADC +where + ADCI: RegisterAccess, +{ + pub fn adc( + peripheral_clock_controller: &mut PeripheralClockControl, + _adc_instance: ADCI, + config: AdcConfig, + ) -> Result { + peripheral_clock_controller.enable(Peripheral::ApbSarAdc); + + let sar_adc = unsafe { &*APB_SARADC::PTR }; + sar_adc.ctrl.modify(|_, w| unsafe { + w.saradc_start_force() + .set_bit() + .saradc_start() + .set_bit() + .saradc_sar_clk_gated() + .set_bit() + .saradc_xpd_sar_force() + .bits(0b11) + }); + let adc = ADC { + adc: PhantomData, + attenuations: config.attenuations, + active_channel: None, + }; + + Ok(adc) + } +} + +impl OneShot for ADC +where + WORD: From, + PIN: Channel, + ADCI: RegisterAccess, +{ + type Error = (); + + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + if self.attenuations[PIN::channel() as usize] == None { + panic!("Channel {} is not configured reading!", PIN::channel()); + } + + if let Some(active_channel) = self.active_channel { + // There is conversion in progress: + // - if it's for a different channel try again later + // - if it's for the given channel, go ahaid and check progress + if active_channel != PIN::channel() { + return Err(nb::Error::WouldBlock); + } + } else { + // If no conversions are in progress, start a new one for given channel + self.active_channel = Some(PIN::channel()); + + let channel = self.active_channel.unwrap(); + let attenuation = self.attenuations[channel as usize].unwrap() as u8; + ADCI::start_onetime_sample(channel, attenuation); + } + + // Wait for ADC to finish conversion + let conversion_finished = ADCI::is_done(); + if !conversion_finished { + return Err(nb::Error::WouldBlock); + } + + // Get converted value + let converted_value = ADCI::read_data(); + ADCI::reset(); + + // There is a hardware limitation. If the APB clock frequency is high, the step + // of this reg signal: ``onetime_start`` may not be captured by the + // ADC digital controller (when its clock frequency is too slow). A rough + // estimate for this step should be at least 3 ADC digital controller + // clock cycle. + // + // This limitation will be removed in hardware future versions. + // We reset ``onetime_start`` in `reset` and assume enough time has passed until + // the next sample is requested. + + // Mark that no conversions are currently in progress + self.active_channel = None; + + Ok(converted_value.into()) + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! impl_adc_interface { + ($adc:ident [ + $( ($pin:ident, $channel:expr) ,)+ + ]) => { + + $( + impl Channel<$adc> for $pin { + type ID = u8; + + fn channel() -> u8 { $channel } + } + )+ + } +} + +pub use impl_adc_interface; diff --git a/esp-hal-common/src/analog/adc/esp32s2.rs b/esp-hal-common/src/analog/adc/esp32s2.rs index 67ec24d8d..575160a07 100644 --- a/esp-hal-common/src/analog/adc/esp32s2.rs +++ b/esp-hal-common/src/analog/adc/esp32s2.rs @@ -56,6 +56,7 @@ impl Default for AdcConfig { } } +#[doc(hidden)] pub trait RegisterAccess { fn set_bit_width(resolution: u8); @@ -360,6 +361,7 @@ where } } +#[doc(hidden)] #[macro_export] macro_rules! impl_adc_interface { ($adc:ident [ diff --git a/esp-hal-common/src/analog/adc/esp32s3.rs b/esp-hal-common/src/analog/adc/esp32s3.rs index 8b1378917..8cf7e7c44 100644 --- a/esp-hal-common/src/analog/adc/esp32s3.rs +++ b/esp-hal-common/src/analog/adc/esp32s3.rs @@ -1 +1 @@ - +// Currently we are missing the SENS peripheral from the SVD diff --git a/esp-hal-common/src/analog/mod.rs b/esp-hal-common/src/analog/mod.rs index 6d03e780c..a276703d1 100644 --- a/esp-hal-common/src/analog/mod.rs +++ b/esp-hal-common/src/analog/mod.rs @@ -34,6 +34,7 @@ cfg_if::cfg_if! { pub dac2: DAC2, } + /// Extension trait to split a SENS peripheral in independent parts pub trait SensExt { fn split(self) -> AvailableAnalog; } @@ -58,3 +59,41 @@ cfg_if::cfg_if! { } } } + +cfg_if::cfg_if! { + if #[cfg(feature = "esp32c3")] { + use core::marker::PhantomData; + + use crate::pac::APB_SARADC; + + pub struct ADC1 { + _private: PhantomData<()>, + } + pub struct ADC2 { + _private: PhantomData<()>, + } + + pub struct AvailableAnalog { + pub adc1: ADC1, + pub adc2: ADC2, + } + + /// Extension trait to split a APB_SARADC peripheral in independent parts + pub trait SarAdcExt { + fn split(self) -> AvailableAnalog; + } + + impl SarAdcExt for APB_SARADC { + fn split(self) -> AvailableAnalog { + AvailableAnalog { + adc1: ADC1 { + _private: PhantomData, + }, + adc2: ADC2 { + _private: PhantomData, + }, + } + } + } + } +} diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index ec4a7678b..9e8ce777f 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -1156,6 +1156,7 @@ pub fn enable_iomux_clk_gate() { } } +#[cfg(not(feature = "esp32c3"))] #[doc(hidden)] #[macro_export] macro_rules! analog { @@ -1213,6 +1214,38 @@ macro_rules! analog { } } +#[cfg(feature = "esp32c3")] +#[doc(hidden)] +#[macro_export] +macro_rules! analog { + ( + $($pxi:ident => $pin_num:literal)+ + ) => { + $( + impl $pxi { + pub fn into_analog(mut self) -> $pxi { + use crate::pac::IO_MUX; + use crate::pac::GPIO; + + let io_mux = unsafe{ &*IO_MUX::PTR }; + let gpio = unsafe{ &*GPIO::PTR }; + + io_mux.gpio[$pin_num].modify(|_,w| unsafe { + w.mcu_sel().bits(1) + .fun_ie().clear_bit() + .fun_wpu().clear_bit() + .fun_wpd().clear_bit() + }); + + gpio.enable_w1tc.write(|w| unsafe { w.bits(1 << $pin_num) }); + + $pxi { _mode: PhantomData } + } + } + )+ + } +} + pub use analog; pub use gpio; pub use impl_errata36; diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index f9592951f..fdb92f473 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -21,6 +21,8 @@ pub enum Peripheral { #[cfg(not(feature = "esp32c3"))] I2cExt1, Rmt, + #[cfg(feature = "esp32c3")] + ApbSarAdc, } /// Controls the enablement of peripheral clocks. @@ -66,6 +68,11 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.rmt_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.rmt_rst().clear_bit()); } + #[cfg(feature = "esp32c3")] + Peripheral::ApbSarAdc => { + perip_clk_en0.modify(|_, w| w.apb_saradc_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.apb_saradc_rst().clear_bit()); + } } } } diff --git a/esp32-hal/examples/adc.rs b/esp32-hal/examples/adc.rs index c6ef637c1..f949178fc 100644 --- a/esp32-hal/examples/adc.rs +++ b/esp32-hal/examples/adc.rs @@ -1,22 +1,19 @@ //! Connect a potentiometer to PIN25 and see the read values change when -//! rotating the shaft. If could also connect the PIN to GND or 3V3 to see the -//! maximum and minimum raw values read. +//! rotating the shaft. Alternatively you could also connect the PIN to GND or +//! 3V3 to see the maximum and minimum raw values read. #![no_std] #![no_main] use esp32_hal::{ + adc::{AdcConfig, Attenuation, ADC, ADC2}, clock::ClockControl, gpio::IO, pac::Peripherals, prelude::*, - AdcConfig, - Attenuation, Delay, RtcCntl, Timer, - ADC, - ADC2, }; use esp_println::println; use panic_halt as _; diff --git a/esp32-hal/src/adc.rs b/esp32-hal/src/adc.rs index 5ff656905..b2ea46b03 100644 --- a/esp32-hal/src/adc.rs +++ b/esp32-hal/src/adc.rs @@ -19,7 +19,8 @@ //! | 9 | | GPIO26 | use embedded_hal::adc::Channel; -use esp_hal_common::analog::{adc::impl_adc_interface, ADC1, ADC2}; +use esp_hal_common::analog::adc::impl_adc_interface; +pub use esp_hal_common::analog::{adc::*, ADC1, ADC2}; use crate::{gpio::*, gpio_types::Analog}; diff --git a/esp32-hal/src/dac.rs b/esp32-hal/src/dac.rs index 16f22445f..1dbfce9ca 100644 --- a/esp32-hal/src/dac.rs +++ b/esp32-hal/src/dac.rs @@ -5,6 +5,7 @@ //! //! The DAC1 is available on the GPIO pin 25, and DAC2 on pin 26. +pub use esp_hal_common::analog::dac::*; use esp_hal_common::{impl_dac, paste}; impl_dac!(1 => Gpio25, 2 => Gpio26,); diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index 719804f42..13688a8f9 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -2,7 +2,6 @@ pub use embedded_hal as ehal; pub use esp_hal_common::{ - analog::{adc::*, *}, clock, cpu_control::CpuControl, efuse, @@ -30,6 +29,11 @@ pub mod adc; pub mod dac; pub mod gpio; +/// Common module for analog functions +pub mod analog { + pub use esp_hal_common::analog::{AvailableAnalog, SensExt}; +} + #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 8091453dd..74a7131b6 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -40,6 +40,7 @@ embedded-graphics = "0.7" panic-halt = "0.2" ssd1306 = "0.7" smart-leds = "0.3" +esp-println = { version = "0.1.0", features = ["esp32c3"] } [features] default = ["rt"] diff --git a/esp32c3-hal/examples/adc.rs b/esp32c3-hal/examples/adc.rs new file mode 100644 index 000000000..31d3248a3 --- /dev/null +++ b/esp32c3-hal/examples/adc.rs @@ -0,0 +1,63 @@ +//! Connect a potentiometer to PIN2 and see the read values change when +//! rotating the shaft. Alternatively you could also connect the PIN to GND or +//! 3V3 to see the maximum and minimum raw values read. + +#![no_std] +#![no_main] + +use esp32c3_hal::{ + adc::{AdcConfig, Attenuation, ADC, ADC1}, + analog::SarAdcExt, + clock::ClockControl, + gpio::IO, + pac::Peripherals, + prelude::*, + system::SystemExt, + Delay, + RtcCntl, + Timer, +}; +use esp_println::println; +use panic_halt as _; +use riscv_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0, clocks.apb_clock); + let mut timer1 = Timer::new(peripherals.TIMG1, clocks.apb_clock); + + rtc_cntl.set_super_wdt_enable(false); + rtc_cntl.set_wdt_enable(false); + timer0.disable(); + timer1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut pin = io.pins.gpio2.into_analog(); + + // Create ADC instances + let analog = peripherals.APB_SARADC.split(); + + let mut adc2_config = AdcConfig::new(); + adc2_config.enable_pin(&pin, Attenuation::Attenuation11dB); + let mut adc2 = ADC::::adc( + &mut system.peripheral_clock_control, + analog.adc1, + adc2_config, + ) + .unwrap(); + + let mut delay = Delay::new(&clocks); + + loop { + let pin_value: u16 = nb::block!(adc2.read(&mut pin)).unwrap(); + println!("PIN ADC reading = {}", pin_value); + delay.delay_ms(1500u32); + } +} diff --git a/esp32c3-hal/src/adc.rs b/esp32c3-hal/src/adc.rs new file mode 100644 index 000000000..4e638b6ff --- /dev/null +++ b/esp32c3-hal/src/adc.rs @@ -0,0 +1,26 @@ +//! Analog to digital (ADC) conversion support. +//! +//! This module provides functions for reading analog values from two +//! analog to digital converters available on the ESP32-C3: `ADC1` and `ADC2`. + +use embedded_hal::adc::Channel; +use esp_hal_common::analog::adc::impl_adc_interface; +pub use esp_hal_common::analog::{adc::*, ADC1, ADC2}; + +use crate::{gpio::*, gpio_types::Analog}; + +impl_adc_interface! { + ADC1 [ + (Gpio0, 0), + (Gpio1, 1), + (Gpio2, 2), + (Gpio3, 3), + (Gpio4, 4), + ] +} + +impl_adc_interface! { + ADC2 [ + (Gpio5, 4), + ] +} diff --git a/esp32c3-hal/src/gpio.rs b/esp32c3-hal/src/gpio.rs index a877adb43..6e6ac1ed1 100644 --- a/esp32c3-hal/src/gpio.rs +++ b/esp32c3-hal/src/gpio.rs @@ -36,3 +36,12 @@ gpio! { Gpio20: (gpio20, 20, gpio[20], IO, 0, Bank0, None), (U0RXD: Function0), (), Gpio21: (gpio21, 21, gpio[21], IO, 0, Bank0, None), (), (U0TXD: Function0), } + +analog! { + Gpio0 => 0 + Gpio1 => 1 + Gpio2 => 2 + Gpio3 => 3 + Gpio4 => 4 + Gpio5 => 4 +} diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 30591334f..79d93ec14 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -30,9 +30,15 @@ use riscv_rt::pre_init; pub use self::{gpio::IO, rtc_cntl::RtcCntl}; +pub mod adc; pub mod gpio; pub mod rtc_cntl; +/// Common module for analog functions +pub mod analog { + pub use esp_hal_common::analog::{AvailableAnalog, SarAdcExt}; +} + extern "C" { // Boundaries of the .iram section static mut _srwtext: u32; diff --git a/esp32s2-hal/examples/adc.rs b/esp32s2-hal/examples/adc.rs index 8bc2b2a44..27c07e593 100644 --- a/esp32s2-hal/examples/adc.rs +++ b/esp32s2-hal/examples/adc.rs @@ -1,24 +1,21 @@ //! Connect a potentiometer to PIN3 and see the read values change when -//! rotating the shaft. If could also connect the PIN to GND or 3V3 to see the -//! maximum and minimum raw values read. -//! +//! rotating the shaft. Alternatively you could also connect the PIN to GND or +//! 3V3 to see the maximum and minimum raw values read. +//! //! THIS CURRENTLY DOESN'T WORK IN DEBUG BUILDS! THIS NEEDS TO GET FIGURED OUT! #![no_std] #![no_main] use esp32s2_hal::{ + adc::{AdcConfig, Attenuation, ADC, ADC1}, clock::ClockControl, gpio::IO, pac::Peripherals, prelude::*, - AdcConfig, - Attenuation, Delay, RtcCntl, Timer, - ADC, - ADC1, }; use esp_println::println; use panic_halt as _; diff --git a/esp32s2-hal/src/adc.rs b/esp32s2-hal/src/adc.rs index 6d04189e4..f3e778287 100644 --- a/esp32s2-hal/src/adc.rs +++ b/esp32s2-hal/src/adc.rs @@ -1,5 +1,11 @@ +//! Analog to digital (ADC) conversion support. +//! +//! This module provides functions for reading analog values from two +//! analog to digital converters available on the ESP32-S2: `ADC1` and `ADC2`. + use embedded_hal::adc::Channel; -use esp_hal_common::analog::{adc::impl_adc_interface, ADC1, ADC2}; +use esp_hal_common::analog::adc::impl_adc_interface; +pub use esp_hal_common::analog::{adc::*, ADC1, ADC2}; use crate::{gpio::*, gpio_types::Analog}; diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 9dc162a85..49a490381 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -2,7 +2,6 @@ pub use embedded_hal as ehal; pub use esp_hal_common::{ - analog::{adc::*, *}, clock, efuse, gpio as gpio_types, @@ -30,6 +29,11 @@ pub mod adc; pub mod dac; pub mod gpio; +/// Common module for analog functions +pub mod analog { + pub use esp_hal_common::analog::{AvailableAnalog, SensExt}; +} + #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {}