esp32: Block ADC2 usage simultaneously with radio (#3876)

* block `ADC2` usage simultaneously with radio (esp32)

* implement `Drop` for `ADC2` (esp32)

fmt

format

* multiple fixes

* make functions internal, lint package

* Cleanup

* use `fetch_or` instead

* take it easy
This commit is contained in:
Kirill Mikhailov 2025-07-30 14:44:16 +02:00 committed by GitHub
parent 4eb4c89944
commit e1917abf9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 78 additions and 3 deletions

View File

@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- PSRAM on ESP32-S2 (#3811)
- WDT now allows configuring longer timeouts (#3816)
- `ADC2` now cannot be used simultaneously with `radio` on ESP32 (#3876)
### Removed

View File

@ -1,10 +1,43 @@
use core::marker::PhantomData;
use core::{
marker::PhantomData,
sync::atomic::{AtomicBool, Ordering},
};
use super::{AdcConfig, Attenuation};
use crate::peripherals::{ADC1, ADC2, RTC_IO, SENS};
use crate::{
peripherals::{ADC1, ADC2, RTC_IO, SENS},
private::{self},
};
pub(super) const NUM_ATTENS: usize = 10;
// ADC2 cannot be used with `radio` functionality on `esp32`, this global helps us to track it's
// state to prevent unexpected behaviour
static ADC2_IN_USE: AtomicBool = AtomicBool::new(false);
/// ADC Error
#[derive(Debug)]
pub enum Error {
/// `ADC2` is used together with `radio`.
Adc2InUse,
}
#[doc(hidden)]
/// Tries to "claim" `ADC2` peripheral and set its status
pub fn try_claim_adc2(_: private::Internal) -> Result<(), Error> {
if ADC2_IN_USE.fetch_or(true, Ordering::Relaxed) {
Err(Error::Adc2InUse)
} else {
Ok(())
}
}
#[doc(hidden)]
/// Resets `ADC2` usage status to `Unused`
pub fn release_adc2(_: private::Internal) {
ADC2_IN_USE.store(false, Ordering::Relaxed);
}
/// The sampling/readout resolution of the ADC.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -44,6 +77,8 @@ pub trait RegisterAccess {
fn read_done_sar() -> bool;
fn read_data_sar() -> u16;
fn instance_number() -> u8;
}
impl RegisterAccess for ADC1<'_> {
@ -119,6 +154,10 @@ impl RegisterAccess for ADC1<'_> {
.meas1_data_sar()
.bits()
}
fn instance_number() -> u8 {
1
}
}
impl RegisterAccess for ADC2<'_> {
@ -194,6 +233,10 @@ impl RegisterAccess for ADC2<'_> {
.meas2_data_sar()
.bits()
}
fn instance_number() -> u8 {
2
}
}
/// Analog-to-Digital Converter peripheral driver.
@ -210,7 +253,18 @@ where
{
/// Configure a given ADC instance using the provided configuration, and
/// initialize the ADC for use
///
/// # Panics
///
/// `ADC2` cannot be used simultaneously with `radio` functionalities, otherwise this function
/// will panic.
pub fn new(adc_instance: ADCI, config: AdcConfig<ADCI>) -> Self {
if ADCI::instance_number() == 2 && try_claim_adc2(private::Internal).is_err() {
panic!(
"ADC2 is already in use by Radio. On ESP32, ADC2 cannot be used simultaneously with Wi-Fi or Bluetooth."
);
}
let sensors = SENS::regs();
// Set reading and sampling resolution
@ -372,3 +426,9 @@ mod adc_implementation {
]
}
}
impl Drop for ADC2<'_> {
fn drop(&mut self) {
release_adc2(private::Internal);
}
}

View File

@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a BLE panic caused by unimplemented functions (#3762)
- Fixed the BLE stack crashing in certain cases (#3854)
- `ADC2` now cannot be used simultaneously with `radio` on ESP32 (#3876)
### Removed

View File

@ -114,6 +114,8 @@ use common_adapter::chip_specific::phy_mem_init;
use esp_config::*;
use esp_hal::{self as hal};
use esp_radio_preempt_driver as preempt;
#[cfg(esp32)]
use hal::analog::adc::{release_adc2, try_claim_adc2};
use hal::{
clock::{Clocks, init_radio_clocks},
time::Rate,
@ -217,6 +219,10 @@ impl Drop for Controller<'_> {
// This shuts down the task switcher and timer tick interrupt.
preempt::disable();
#[cfg(esp32)]
// Allow using `ADC2` again
release_adc2(unsafe { esp_hal::Internal::conjure() });
}
}
@ -224,6 +230,11 @@ impl Drop for Controller<'_> {
///
/// Make sure to **not** call this function while interrupts are disabled.
pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
#[cfg(esp32)]
if try_claim_adc2(unsafe { hal::Internal::conjure() }).is_err() {
return Err(InitializationError::Adc2IsUsed);
}
if crate::is_interrupts_disabled() {
return Err(InitializationError::InterruptsDisabled);
}
@ -294,6 +305,9 @@ pub enum InitializationError {
InterruptsDisabled,
/// The scheduler is not initialized.
SchedulerNotInitialized,
#[cfg(esp32)]
// ADC2 cannot be used with `radio` functionality on `esp32`.
Adc2IsUsed,
}
#[cfg(feature = "wifi")]