Implement calibrated ADC API for S3 (#641)
* adc_cal: s3: Add efuse functions for reading calibration * Add changelog entry * Implement calibrated ADC API for S3 * adc_cal: s3: Add calibrated ADC reading example * Clean up * Prefer where clauses * Clean up unnecessary unsafe blocks * Fix autolinks --------- Co-authored-by: Scott Mabin <scott@mabez.dev>
This commit is contained in:
parent
10ec2647eb
commit
2472b6d7a9
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add initial LP-IO support for ESP32-C6 (#639)
|
||||
- Implement sleep with some wakeup methods for `esp32` (#574)
|
||||
- Add a new RMT driver (#653, #667)
|
||||
- Implemented calibrated ADC API for ESP32-S3 (#641)
|
||||
- Add MCPWM DeadTime configuration (#406)
|
||||
|
||||
### Changed
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::adc::{AdcCalEfuse, AdcCalScheme, AdcCalSource, AdcConfig, Attenuation, RegisterAccess};
|
||||
use crate::adc::{
|
||||
AdcCalEfuse,
|
||||
AdcCalScheme,
|
||||
AdcCalSource,
|
||||
AdcConfig,
|
||||
Attenuation,
|
||||
CalibrationAccess,
|
||||
};
|
||||
|
||||
/// Basic ADC calibration scheme
|
||||
///
|
||||
@ -18,7 +25,7 @@ pub struct AdcCalBasic<ADCI> {
|
||||
|
||||
impl<ADCI> AdcCalScheme<ADCI> for AdcCalBasic<ADCI>
|
||||
where
|
||||
ADCI: AdcCalEfuse + RegisterAccess,
|
||||
ADCI: AdcCalEfuse + CalibrationAccess,
|
||||
{
|
||||
fn new_cal(atten: Attenuation) -> Self {
|
||||
// Try to get init code (Dout0) from efuse
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::adc::{
|
||||
AdcCalScheme,
|
||||
AdcHasLineCal,
|
||||
Attenuation,
|
||||
RegisterAccess,
|
||||
CalibrationAccess,
|
||||
};
|
||||
|
||||
const COEFF_MUL: i64 = 1 << 52;
|
||||
@ -52,7 +52,7 @@ pub struct AdcCalCurve<ADCI> {
|
||||
|
||||
impl<ADCI> AdcCalScheme<ADCI> for AdcCalCurve<ADCI>
|
||||
where
|
||||
ADCI: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + RegisterAccess,
|
||||
ADCI: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + CalibrationAccess,
|
||||
{
|
||||
fn new_cal(atten: Attenuation) -> Self {
|
||||
let line = AdcCalLine::<ADCI>::new_cal(atten);
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::adc::{
|
||||
AdcCalSource,
|
||||
AdcConfig,
|
||||
Attenuation,
|
||||
RegisterAccess,
|
||||
CalibrationAccess,
|
||||
};
|
||||
|
||||
/// Marker trait for ADC units which support line fitting
|
||||
@ -46,7 +46,7 @@ pub struct AdcCalLine<ADCI> {
|
||||
|
||||
impl<ADCI> AdcCalScheme<ADCI> for AdcCalLine<ADCI>
|
||||
where
|
||||
ADCI: AdcCalEfuse + AdcHasLineCal + RegisterAccess,
|
||||
ADCI: AdcCalEfuse + AdcHasLineCal + CalibrationAccess,
|
||||
{
|
||||
fn new_cal(atten: Attenuation) -> Self {
|
||||
let basic = AdcCalBasic::<ADCI>::new_cal(atten);
|
||||
@ -93,8 +93,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6))]
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))]
|
||||
impl AdcHasLineCal for crate::adc::ADC1 {}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
#[cfg(any(esp32c3, esp32s3))]
|
||||
impl AdcHasLineCal for crate::adc::ADC2 {}
|
||||
|
||||
@ -60,7 +60,7 @@ cfg_if::cfg_if! {
|
||||
|
||||
const ADC_VAL_MASK: u16 = 0xfff;
|
||||
const ADC_CAL_CNT_MAX: u16 = 32;
|
||||
const ADC_CAL_CHANNEL: u32 = 0xf;
|
||||
const ADC_CAL_CHANNEL: u16 = 15;
|
||||
|
||||
const ADC_SAR1_ENCAL_GND_ADDR: u8 = 0x7;
|
||||
const ADC_SAR1_ENCAL_GND_ADDR_MSB: u8 = 5;
|
||||
@ -166,7 +166,10 @@ pub struct AdcPin<PIN, ADCI, CS = ()> {
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<PIN: Channel<ADCI, ID = u8>, ADCI, CS> Channel<ADCI> for AdcPin<PIN, ADCI, CS> {
|
||||
impl<PIN, ADCI, CS> Channel<ADCI> for AdcPin<PIN, ADCI, CS>
|
||||
where
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
{
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
@ -188,11 +191,10 @@ where
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI, ()> {
|
||||
pub fn enable_pin<PIN>(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin<PIN, ADCI>
|
||||
where
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
{
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
@ -202,11 +204,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_pin_with_cal<PIN: Channel<ADCI, ID = u8>, CS: AdcCalScheme<ADCI>>(
|
||||
pub fn enable_pin_with_cal<PIN, CS>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI, CS> {
|
||||
) -> AdcPin<PIN, ADCI, CS>
|
||||
where
|
||||
ADCI: CalibrationAccess,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
CS: AdcCalScheme<ADCI>,
|
||||
{
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
@ -217,7 +224,10 @@ where
|
||||
}
|
||||
|
||||
/// Calibrate ADC with specified attenuation and voltage source
|
||||
pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16 {
|
||||
pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16
|
||||
where
|
||||
ADCI: CalibrationAccess,
|
||||
{
|
||||
let mut adc_max: u16 = 0;
|
||||
let mut adc_min: u16 = u16::MAX;
|
||||
let mut adc_sum: u32 = 0;
|
||||
@ -290,15 +300,21 @@ pub trait RegisterAccess {
|
||||
/// Reset flags
|
||||
fn reset();
|
||||
|
||||
fn enable_vdef(enable: bool);
|
||||
|
||||
/// Enable internal connect GND (for calibration)
|
||||
fn connect_cal(source: AdcCalSource, enable: bool);
|
||||
|
||||
/// Set calibration parameter to ADC hardware
|
||||
fn set_init_code(data: u16);
|
||||
}
|
||||
|
||||
pub trait CalibrationAccess: RegisterAccess {
|
||||
const ADC_CAL_CNT_MAX: u16;
|
||||
const ADC_CAL_CHANNEL: u16;
|
||||
const ADC_VAL_MASK: u16;
|
||||
|
||||
fn enable_vdef(enable: bool);
|
||||
|
||||
/// Enable internal calibration voltage source
|
||||
fn connect_cal(source: AdcCalSource, enable: bool);
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC1 {
|
||||
fn config_onetime_sample(channel: u8, attenuation: u8) {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
@ -347,6 +363,33 @@ impl RegisterAccess for ADC1 {
|
||||
.modify(|_, w| w.saradc_onetime_start().clear_bit());
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB,
|
||||
msb as _,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB,
|
||||
lsb as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl CalibrationAccess for ADC1 {
|
||||
const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
|
||||
const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
|
||||
const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
|
||||
|
||||
fn enable_vdef(enable: bool) {
|
||||
let value = enable as _;
|
||||
regi2c_write_mask(
|
||||
@ -380,27 +423,6 @@ impl RegisterAccess for ADC1 {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB,
|
||||
ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB,
|
||||
msb as _,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB,
|
||||
ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB,
|
||||
lsb as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
@ -450,6 +472,34 @@ impl RegisterAccess for ADC2 {
|
||||
.modify(|_, w| w.saradc_onetime_start().clear_bit());
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB,
|
||||
msb as _,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB,
|
||||
lsb as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
impl CalibrationAccess for ADC2 {
|
||||
const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
|
||||
const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
|
||||
const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
|
||||
|
||||
fn enable_vdef(enable: bool) {
|
||||
let value = enable as _;
|
||||
regi2c_write_mask(
|
||||
@ -483,27 +533,6 @@ impl RegisterAccess for ADC2 {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB,
|
||||
ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB,
|
||||
msb as _,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_SAR_ADC,
|
||||
I2C_SAR_ADC_HOSTID,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB,
|
||||
ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB,
|
||||
lsb as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC<'d, ADCI> {
|
||||
|
||||
@ -2,12 +2,88 @@ use core::marker::PhantomData;
|
||||
|
||||
use embedded_hal::adc::{Channel, OneShot};
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
use crate::efuse::Efuse;
|
||||
use crate::{
|
||||
analog::{ADC1, ADC2},
|
||||
peripheral::PeripheralRef,
|
||||
peripherals::{APB_SARADC, SENS},
|
||||
};
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
mod cal_basic;
|
||||
#[cfg(esp32s3)]
|
||||
mod cal_curve;
|
||||
#[cfg(esp32s3)]
|
||||
mod cal_line;
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
pub use cal_basic::AdcCalBasic;
|
||||
#[cfg(esp32s3)]
|
||||
pub use cal_curve::{AdcCalCurve, AdcHasCurveCal};
|
||||
#[cfg(esp32s3)]
|
||||
pub use cal_line::{AdcCalLine, AdcHasLineCal};
|
||||
|
||||
pub use crate::analog::{AdcCalEfuse, AdcCalScheme};
|
||||
|
||||
// Constants taken from:
|
||||
// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32s2/include/soc/regi2c_saradc.h
|
||||
// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32s3/include/soc/regi2c_saradc.h
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32s2, esp32s3))] {
|
||||
const I2C_SAR_ADC: u8 = 0x69;
|
||||
const I2C_SAR_ADC_HOSTID: u8 = 1;
|
||||
|
||||
const ADC_VAL_MASK: u16 = 0xfff;
|
||||
const ADC_CAL_CNT_MAX: u16 = 32;
|
||||
const ADC_CAL_CHANNEL: u16 = 15;
|
||||
|
||||
const ADC_SAR1_ENCAL_GND_ADDR: u8 = 0x7;
|
||||
const ADC_SAR1_ENCAL_GND_ADDR_MSB: u8 = 5;
|
||||
const ADC_SAR1_ENCAL_GND_ADDR_LSB: u8 = 5;
|
||||
|
||||
const ADC_SAR1_INITIAL_CODE_HIGH_ADDR: u8 = 0x1;
|
||||
const ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB: u8 = 0x3;
|
||||
const ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB: u8 = 0x0;
|
||||
|
||||
const ADC_SAR1_INITIAL_CODE_LOW_ADDR: u8 = 0x0;
|
||||
const ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB: u8 = 0x7;
|
||||
const ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB: u8 = 0x0;
|
||||
|
||||
const ADC_SAR1_DREF_ADDR: u8 = 0x2;
|
||||
const ADC_SAR1_DREF_ADDR_MSB: u8 = 0x6;
|
||||
const ADC_SAR1_DREF_ADDR_LSB: u8 = 0x4;
|
||||
|
||||
const ADC_SARADC1_ENCAL_REF_ADDR: u8 = 0x7;
|
||||
const ADC_SARADC1_ENCAL_REF_ADDR_MSB: u8 = 4;
|
||||
const ADC_SARADC1_ENCAL_REF_ADDR_LSB: u8 = 4;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32s2, esp32s3))] {
|
||||
const ADC_SAR2_ENCAL_GND_ADDR: u8 = 0x7;
|
||||
const ADC_SAR2_ENCAL_GND_ADDR_MSB: u8 = 5;
|
||||
const ADC_SAR2_ENCAL_GND_ADDR_LSB: u8 = 5;
|
||||
|
||||
const ADC_SAR2_INITIAL_CODE_HIGH_ADDR: u8 = 0x4;
|
||||
const ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB: u8 = 0x3;
|
||||
const ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB: u8 = 0x0;
|
||||
|
||||
const ADC_SAR2_INITIAL_CODE_LOW_ADDR: u8 = 0x3;
|
||||
const ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB: u8 = 0x7;
|
||||
const ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB: u8 = 0x0;
|
||||
|
||||
const ADC_SAR2_DREF_ADDR: u8 = 0x5;
|
||||
const ADC_SAR2_DREF_ADDR_MSB: u8 = 0x6;
|
||||
const ADC_SAR2_DREF_ADDR_LSB: u8 = 0x4;
|
||||
|
||||
const ADC_SARADC2_ENCAL_REF_ADDR: u8 = 0x7;
|
||||
const ADC_SARADC2_ENCAL_REF_ADDR_MSB: u8 = 4;
|
||||
const ADC_SARADC2_ENCAL_REF_ADDR_LSB: u8 = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/// The sampling/readout resolution of the ADC
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Resolution {
|
||||
@ -17,18 +93,53 @@ pub enum Resolution {
|
||||
/// The attenuation of the ADC pin
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Attenuation {
|
||||
/// 0 dB attenuation, measurement range: 0 - 800 mV
|
||||
Attenuation0dB = 0b00,
|
||||
/// 2.5 dB attenuation, measurement range: 0 - 1100 mV
|
||||
Attenuation2p5dB = 0b01,
|
||||
/// 6 dB attenuation, measurement range: 0 - 1350 mV
|
||||
Attenuation6dB = 0b10,
|
||||
/// 11 dB attenuation, measurement range: 0 - 2600 mV
|
||||
Attenuation11dB = 0b11,
|
||||
}
|
||||
|
||||
pub struct AdcPin<PIN, ADCI> {
|
||||
impl Attenuation {
|
||||
/// List of all supported attenuations
|
||||
pub const ALL: &'static [Attenuation] = &[
|
||||
Attenuation::Attenuation0dB,
|
||||
Attenuation::Attenuation2p5dB,
|
||||
Attenuation::Attenuation6dB,
|
||||
Attenuation::Attenuation11dB,
|
||||
];
|
||||
|
||||
/// Reference voltage in millivolts
|
||||
///
|
||||
/// Vref = 10 ^ (Att / 20) * Vref0
|
||||
/// where Vref0 = 1.1 V, Att - attenuation in dB
|
||||
///
|
||||
/// To convert raw value to millivolts use formula:
|
||||
/// V = D * Vref / 2 ^ R
|
||||
/// where D - raw ADC value, R - resolution in bits
|
||||
pub const fn ref_mv(&self) -> u16 {
|
||||
match self {
|
||||
Attenuation::Attenuation0dB => 1100,
|
||||
Attenuation::Attenuation2p5dB => 1467,
|
||||
Attenuation::Attenuation6dB => 2195,
|
||||
Attenuation::Attenuation11dB => 3903,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdcPin<PIN, ADCI, CS = ()> {
|
||||
pub pin: PIN,
|
||||
pub cal_scheme: CS,
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<PIN: Channel<ADCI, ID = u8>, ADCI> Channel<ADCI> for AdcPin<PIN, ADCI> {
|
||||
impl<PIN, ADCI, CS> Channel<ADCI> for AdcPin<PIN, ADCI, CS>
|
||||
where
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
{
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
@ -50,18 +161,82 @@ where
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI> {
|
||||
pub fn enable_pin<PIN>(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin<PIN, ADCI>
|
||||
where
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
{
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
pin,
|
||||
cal_scheme: AdcCalScheme::<()>::new_cal(attenuation),
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_pin_with_cal<PIN, CS>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI, CS>
|
||||
where
|
||||
ADCI: CalibrationAccess,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
CS: AdcCalScheme<ADCI>,
|
||||
{
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
pin,
|
||||
cal_scheme: CS::new_cal(attenuation),
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrate ADC with specified attenuation and voltage source
|
||||
pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16
|
||||
where
|
||||
ADCI: CalibrationAccess,
|
||||
{
|
||||
let mut adc_max: u16 = 0;
|
||||
let mut adc_min: u16 = u16::MAX;
|
||||
let mut adc_sum: u32 = 0;
|
||||
|
||||
ADCI::enable_vdef(true);
|
||||
|
||||
// Start sampling
|
||||
ADCI::adc_samplecfg(ADCI::ADC_CAL_CHANNEL);
|
||||
ADCI::set_attenuation(ADCI::ADC_CAL_CHANNEL as usize, atten as u8);
|
||||
|
||||
// Connect calibration source
|
||||
ADCI::connect_cal(source, true);
|
||||
|
||||
for _ in 0..ADCI::ADC_CAL_CNT_MAX {
|
||||
ADCI::set_init_code(0);
|
||||
|
||||
// Trigger ADC sampling
|
||||
ADCI::start_sample();
|
||||
|
||||
// Wait until ADC1 sampling is done
|
||||
while !ADCI::is_done() {}
|
||||
|
||||
let adc = ADCI::read_data() & ADCI::ADC_VAL_MASK;
|
||||
|
||||
ADCI::reset();
|
||||
|
||||
adc_sum += adc as u32;
|
||||
adc_max = adc.max(adc_max);
|
||||
adc_min = adc.min(adc_min);
|
||||
}
|
||||
|
||||
let cal_val =
|
||||
(adc_sum - adc_max as u32 - adc_min as u32) as u16 / (ADCI::ADC_CAL_CNT_MAX - 2);
|
||||
|
||||
// Disconnect calibration source
|
||||
ADCI::connect_cal(source, false);
|
||||
|
||||
cal_val
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADCI> Default for AdcConfig<ADCI> {
|
||||
@ -74,11 +249,15 @@ impl<ADCI> Default for AdcConfig<ADCI> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcCalSource {
|
||||
Gnd,
|
||||
Ref,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait RegisterAccess {
|
||||
fn set_bit_width(resolution: u8);
|
||||
|
||||
fn set_sample_bit(resolution: u8);
|
||||
fn adc_samplecfg(channel: u16);
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8);
|
||||
|
||||
@ -90,22 +269,52 @@ pub trait RegisterAccess {
|
||||
|
||||
fn set_en_pad(channel: u8);
|
||||
|
||||
fn clear_start_sar();
|
||||
fn clear_start_sample();
|
||||
|
||||
fn set_start_sar();
|
||||
fn start_sample();
|
||||
|
||||
fn read_done_sar() -> bool;
|
||||
/// Check if sampling is done
|
||||
fn is_done() -> bool;
|
||||
|
||||
fn read_data_sar() -> u16;
|
||||
/// Read sample data
|
||||
fn read_data() -> u16;
|
||||
|
||||
/// Set calibration parameter to ADC hardware
|
||||
fn set_init_code(data: u16);
|
||||
|
||||
/// Reset flags
|
||||
fn reset();
|
||||
}
|
||||
|
||||
pub trait CalibrationAccess: RegisterAccess {
|
||||
const ADC_CAL_CNT_MAX: u16;
|
||||
const ADC_CAL_CHANNEL: u16;
|
||||
const ADC_VAL_MASK: u16;
|
||||
|
||||
fn enable_vdef(enable: bool);
|
||||
|
||||
/// Enable internal calibration voltage source
|
||||
fn connect_cal(source: AdcCalSource, enable: bool);
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC1 {
|
||||
fn set_bit_width(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
fn adc_samplecfg(channel: u16) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
fn set_sample_bit(_resolution: u8) {
|
||||
// no-op
|
||||
// Configure for RTC control
|
||||
sensors.sar_meas1_mux.modify(|_r, w| {
|
||||
w.sar1_dig_force().clear_bit() // 1: Select digital control;
|
||||
// 0: Select RTC control.
|
||||
});
|
||||
sensors.sar_meas1_ctrl2.modify(|_r, w| {
|
||||
w.meas1_start_force()
|
||||
.set_bit() // 1: SW control RTC ADC start; 0: ULP control RTC ADC start.
|
||||
.sar1_en_pad_force()
|
||||
.set_bit() // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map;
|
||||
// Enable internal connect GND (for calibration).
|
||||
.sar1_en_pad()
|
||||
.variant(channel) // only one channel is selected.
|
||||
});
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
@ -146,38 +355,93 @@ impl RegisterAccess for ADC1 {
|
||||
.modify(|_, w| unsafe { w.sar1_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
fn clear_start_sample() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
fn start_sample() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
fn is_done() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas1_ctrl2.read().meas1_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
fn read_data() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas1_ctrl2.read().meas1_data_sar().bits() as u16
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_INITIAL_CODE_HIGH_ADDR, msb as u32);
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_INITIAL_CODE_LOW_ADDR, lsb as u32);
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
let adc = unsafe { &*APB_SARADC::ptr() };
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
adc.int_clr
|
||||
.write(|w| w.adc1_done_int_clr().set_bit());
|
||||
} else {
|
||||
adc.int_clr
|
||||
.write(|w| w.apb_saradc1_done_int_clr().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_sar().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
impl CalibrationAccess for ADC1 {
|
||||
const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
|
||||
const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
|
||||
const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
|
||||
|
||||
fn enable_vdef(enable: bool) {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_DREF_ADDR, enable as u8);
|
||||
}
|
||||
|
||||
fn connect_cal(source: AdcCalSource, enable: bool) {
|
||||
match source {
|
||||
AdcCalSource::Gnd => {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_ENCAL_GND_ADDR, enable as u8);
|
||||
}
|
||||
AdcCalSource::Ref => {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SARADC1_ENCAL_REF_ADDR, enable as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC2 {
|
||||
fn set_bit_width(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
fn adc_samplecfg(channel: u16) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
fn set_sample_bit(_resolution: u8) {
|
||||
// no-op
|
||||
// Configure for RTC control
|
||||
sensors.sar_meas2_ctrl2.modify(|_r, w| {
|
||||
w.meas2_start_force()
|
||||
.set_bit() // 1: SW control RTC ADC start; 0: ULP control RTC ADC start.
|
||||
.sar2_en_pad_force()
|
||||
.set_bit() // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map;
|
||||
// Enable internal connect GND (for calibration).
|
||||
.sar2_en_pad()
|
||||
.variant(channel) // only one channel is selected.
|
||||
});
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
@ -223,29 +487,77 @@ impl RegisterAccess for ADC2 {
|
||||
.modify(|_, w| unsafe { w.sar2_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
fn clear_start_sample() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
fn start_sample() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
fn is_done() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas2_ctrl2.read().meas2_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
fn read_data() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas2_ctrl2.read().meas2_data_sar().bits() as u16
|
||||
}
|
||||
|
||||
fn set_init_code(data: u16) {
|
||||
let [msb, lsb] = data.to_be_bytes();
|
||||
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_INITIAL_CODE_HIGH_ADDR, msb as u32);
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_INITIAL_CODE_LOW_ADDR, lsb as u32);
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
let adc = unsafe { &*APB_SARADC::ptr() };
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
adc.int_clr
|
||||
.write(|w| w.adc2_done_int_clr().set_bit());
|
||||
} else {
|
||||
adc.int_clr
|
||||
.write(|w| w.apb_saradc2_done_int_clr().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_sar().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
impl CalibrationAccess for ADC2 {
|
||||
const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
|
||||
const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
|
||||
const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
|
||||
|
||||
fn enable_vdef(enable: bool) {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_DREF_ADDR, enable as u8);
|
||||
}
|
||||
|
||||
fn connect_cal(source: AdcCalSource, enable: bool) {
|
||||
match source {
|
||||
AdcCalSource::Gnd => {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_ENCAL_GND_ADDR, enable as u8);
|
||||
}
|
||||
AdcCalSource::Ref => {
|
||||
crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SARADC2_ENCAL_REF_ADDR, enable as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC<'d, ADC> {
|
||||
@ -264,12 +576,6 @@ where
|
||||
) -> Result<Self, ()> {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
// Set reading and sampling resolution
|
||||
let resolution: u8 = config.resolution as u8;
|
||||
|
||||
ADCI::set_bit_width(resolution);
|
||||
ADCI::set_sample_bit(resolution);
|
||||
|
||||
// Set attenuation for pins
|
||||
let attenuations = config.attenuations;
|
||||
|
||||
@ -342,15 +648,46 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADCI, WORD, PIN> OneShot<ADCI, WORD, AdcPin<PIN, ADCI>> for ADC<'d, ADCI>
|
||||
#[cfg(esp32s3)]
|
||||
impl AdcCalEfuse for ADC1 {
|
||||
fn get_init_code(atten: Attenuation) -> Option<u16> {
|
||||
Efuse::get_rtc_calib_init_code(1, atten)
|
||||
}
|
||||
|
||||
fn get_cal_mv(atten: Attenuation) -> u16 {
|
||||
Efuse::get_rtc_calib_cal_mv(1, atten)
|
||||
}
|
||||
|
||||
fn get_cal_code(atten: Attenuation) -> Option<u16> {
|
||||
Efuse::get_rtc_calib_cal_code(1, atten)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
impl AdcCalEfuse for ADC2 {
|
||||
fn get_init_code(atten: Attenuation) -> Option<u16> {
|
||||
Efuse::get_rtc_calib_init_code(2, atten)
|
||||
}
|
||||
|
||||
fn get_cal_mv(atten: Attenuation) -> u16 {
|
||||
Efuse::get_rtc_calib_cal_mv(2, atten)
|
||||
}
|
||||
|
||||
fn get_cal_code(atten: Attenuation) -> Option<u16> {
|
||||
Efuse::get_rtc_calib_cal_code(2, atten)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADCI, WORD, PIN, CS> OneShot<ADCI, WORD, AdcPin<PIN, ADCI, CS>> for ADC<'d, ADCI>
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
ADCI: RegisterAccess,
|
||||
CS: AdcCalScheme<ADCI>,
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self, _pin: &mut AdcPin<PIN, ADCI>) -> nb::Result<WORD, Self::Error> {
|
||||
fn read(&mut self, pin: &mut AdcPin<PIN, ADCI, CS>) -> nb::Result<WORD, Self::Error> {
|
||||
if self.attenuations[AdcPin::<PIN, ADCI>::channel() as usize] == None {
|
||||
panic!(
|
||||
"Channel {} is not configured reading!",
|
||||
@ -369,20 +706,24 @@ where
|
||||
// If no conversions are in progress, start a new one for given channel
|
||||
self.active_channel = Some(AdcPin::<PIN, ADCI>::channel());
|
||||
|
||||
// Set ADC unit calibration according used scheme for pin
|
||||
ADCI::set_init_code(pin.cal_scheme.adc_cal());
|
||||
|
||||
ADCI::set_en_pad(AdcPin::<PIN, ADCI>::channel() as u8);
|
||||
|
||||
ADCI::clear_start_sar();
|
||||
ADCI::set_start_sar();
|
||||
ADCI::clear_start_sample();
|
||||
ADCI::start_sample();
|
||||
}
|
||||
|
||||
// Wait for ADC to finish conversion
|
||||
let conversion_finished = ADCI::read_done_sar();
|
||||
let conversion_finished = ADCI::is_done();
|
||||
if !conversion_finished {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
let converted_value = ADCI::read_data_sar();
|
||||
let converted_value = ADCI::read_data();
|
||||
ADCI::reset();
|
||||
|
||||
// Mark that no conversions are currently in progress
|
||||
self.active_channel = None;
|
||||
|
||||
@ -2,7 +2,6 @@ use crate::{
|
||||
clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock},
|
||||
regi2c_write,
|
||||
regi2c_write_mask,
|
||||
rom::{rom_i2c_writeReg, rom_i2c_writeReg_Mask},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
@ -43,18 +42,18 @@ const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 3;
|
||||
pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllClock) {
|
||||
let system = unsafe { &*crate::peripherals::SYSTEM::ptr() };
|
||||
|
||||
unsafe {
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let dr1: u32;
|
||||
let dr3: u32;
|
||||
let dchgp: u32;
|
||||
let dcur: u32;
|
||||
let dbias: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let dr1: u32;
|
||||
let dr3: u32;
|
||||
let dchgp: u32;
|
||||
let dcur: u32;
|
||||
let dbias: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
|
||||
unsafe {
|
||||
let clear_reg_mask = |reg, mask: u32| {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask)
|
||||
};
|
||||
@ -64,53 +63,52 @@ pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllCl
|
||||
|
||||
clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH);
|
||||
set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW);
|
||||
|
||||
// Set this register to let the digital part know 480M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().set_bit());
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 236;
|
||||
dr1 = 4;
|
||||
dr3 = 4;
|
||||
dchgp = 0;
|
||||
dcur = 0;
|
||||
dbias = 2;
|
||||
}
|
||||
XtalClock::RtcXtalFreq40M | XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
|
||||
|
||||
i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur =
|
||||
(1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
|
||||
}
|
||||
|
||||
// Set this register to let the digital part know 480M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().set_bit());
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 236;
|
||||
dr1 = 4;
|
||||
dr3 = 4;
|
||||
dchgp = 0;
|
||||
dcur = 0;
|
||||
dbias = 2;
|
||||
}
|
||||
XtalClock::RtcXtalFreq40M | XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
|
||||
|
||||
i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c2_rtc_bbpll_enable() {
|
||||
|
||||
@ -2,7 +2,6 @@ use crate::{
|
||||
clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock},
|
||||
regi2c_write,
|
||||
regi2c_write_mask,
|
||||
rom::{rom_i2c_writeReg, rom_i2c_writeReg_Mask},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
@ -47,18 +46,17 @@ const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 2;
|
||||
pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) {
|
||||
let system = unsafe { &*crate::peripherals::SYSTEM::ptr() };
|
||||
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let dr1: u32;
|
||||
let dr3: u32;
|
||||
let dchgp: u32;
|
||||
let dcur: u32;
|
||||
let dbias: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
unsafe {
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let dr1: u32;
|
||||
let dr3: u32;
|
||||
let dchgp: u32;
|
||||
let dcur: u32;
|
||||
let dbias: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
|
||||
let clear_reg_mask = |reg, mask: u32| {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask)
|
||||
};
|
||||
@ -68,110 +66,109 @@ pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClo
|
||||
|
||||
clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH);
|
||||
set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW);
|
||||
}
|
||||
|
||||
if matches!(pll_freq, PllClock::Pll480MHz) {
|
||||
// Set this register to let the digital part know 480M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().set_bit());
|
||||
if matches!(pll_freq, PllClock::Pll480MHz) {
|
||||
// Set this register to let the digital part know 480M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().set_bit());
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 26;
|
||||
dr1 = 1;
|
||||
dr3 = 1;
|
||||
dchgp = 4;
|
||||
dcur = 0;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
|
||||
} else {
|
||||
// Clear this register to let the digital part know 320M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().clear_bit());
|
||||
|
||||
// Configure 320M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 6;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 26;
|
||||
dr1 = 1;
|
||||
dr3 = 1;
|
||||
dchgp = 4;
|
||||
dcur = 0;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69);
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur =
|
||||
(2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
|
||||
} else {
|
||||
// Clear this register to let the digital part know 320M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().clear_bit());
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
|
||||
// Configure 320M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 6;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69);
|
||||
}
|
||||
|
||||
i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur = (2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1);
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c3_rtc_bbpll_enable() {
|
||||
|
||||
@ -25,11 +25,14 @@ extern "C" {
|
||||
macro_rules! regi2c_write {
|
||||
( $block: ident, $reg_add: ident, $indata: expr ) => {
|
||||
paste::paste! {
|
||||
rom_i2c_writeReg($block,
|
||||
[<$block _HOSTID>],
|
||||
$reg_add,
|
||||
$indata
|
||||
);
|
||||
unsafe {
|
||||
crate::rom::rom_i2c_writeReg(
|
||||
$block as u32,
|
||||
[<$block _HOSTID>] as u32,
|
||||
$reg_add as u32,
|
||||
$indata as u32
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -39,13 +42,16 @@ macro_rules! regi2c_write {
|
||||
macro_rules! regi2c_write_mask {
|
||||
( $block: ident, $reg_add: ident, $indata: expr ) => {
|
||||
paste::paste! {
|
||||
rom_i2c_writeReg_Mask($block,
|
||||
[<$block _HOSTID>],
|
||||
$reg_add,
|
||||
[<$reg_add _MSB>],
|
||||
[<$reg_add _LSB>],
|
||||
$indata
|
||||
);
|
||||
unsafe {
|
||||
crate::rom::rom_i2c_writeReg_Mask(
|
||||
$block as u32,
|
||||
[<$block _HOSTID>] as u32,
|
||||
$reg_add as u32,
|
||||
[<$reg_add _MSB>] as u32,
|
||||
[<$reg_add _LSB>] as u32,
|
||||
$indata as u32
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ use crate::{
|
||||
clock::XtalClock,
|
||||
peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM},
|
||||
regi2c_write_mask,
|
||||
rom::rom_i2c_writeReg_Mask,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
@ -29,10 +28,8 @@ const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2;
|
||||
pub(crate) fn init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
}
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
unsafe {
|
||||
rtc_cntl
|
||||
@ -53,9 +50,9 @@ pub(crate) fn init() {
|
||||
unsafe {
|
||||
rtc_cntl.int_ena_rtc.write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr_rtc.write(|w| w.bits(u32::MAX));
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
|
||||
@ -4,7 +4,6 @@ use crate::{
|
||||
clock::XtalClock,
|
||||
peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM},
|
||||
regi2c_write_mask,
|
||||
rom::rom_i2c_writeReg_Mask,
|
||||
rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock},
|
||||
};
|
||||
|
||||
@ -29,11 +28,9 @@ const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2;
|
||||
pub(crate) fn init() {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0);
|
||||
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
}
|
||||
regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0);
|
||||
|
||||
rtc_cntl.ana_conf.modify(|_, w| w.pvtmon_pu().clear_bit());
|
||||
|
||||
@ -83,9 +80,9 @@ pub(crate) fn init() {
|
||||
unsafe {
|
||||
rtc_cntl.int_ena_rtc.write(|w| w.bits(0));
|
||||
rtc_cntl.int_clr_rtc.write(|w| w.bits(u32::MAX));
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0);
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Reading of eFuses
|
||||
|
||||
use crate::peripherals::EFUSE;
|
||||
pub use crate::soc::efuse_field::*;
|
||||
use crate::{analog::adc::Attenuation, peripherals::EFUSE};
|
||||
|
||||
pub struct Efuse;
|
||||
|
||||
@ -36,6 +36,135 @@ impl Efuse {
|
||||
pub fn get_rwdt_multiplier() -> u8 {
|
||||
Self::read_field_le::<u8>(WDT_DELAY_SEL)
|
||||
}
|
||||
|
||||
/// Get efuse block version
|
||||
///
|
||||
/// see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/efuse_hal.c#L27-L30>
|
||||
pub fn get_block_version() -> (u8, u8) {
|
||||
// see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/esp32s3/include/hal/efuse_ll.h#L65-L73>
|
||||
// <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32s3/esp_efuse_table.csv#L196>
|
||||
(
|
||||
Self::read_field_le::<u8>(BLK_VERSION_MAJOR),
|
||||
Self::read_field_le::<u8>(BLK_VERSION_MINOR),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get version of RTC calibration block
|
||||
///
|
||||
/// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32s3/esp_efuse_rtc_calib.c#L15>
|
||||
pub fn get_rtc_calib_version() -> u8 {
|
||||
let (major, _minor) = Self::get_block_version();
|
||||
|
||||
if major == 1 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Get ADC initial code for specified attenuation from efuse
|
||||
///
|
||||
/// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32s3/esp_efuse_rtc_calib.c#L28>
|
||||
pub fn get_rtc_calib_init_code(unit: u8, atten: Attenuation) -> Option<u16> {
|
||||
let version = Self::get_rtc_calib_version();
|
||||
|
||||
if version != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let adc_icode_diff: [u16; 4] = if unit == 0 {
|
||||
[
|
||||
Self::read_field_le(ADC1_INIT_CODE_ATTEN0),
|
||||
Self::read_field_le(ADC1_INIT_CODE_ATTEN1),
|
||||
Self::read_field_le(ADC1_INIT_CODE_ATTEN2),
|
||||
Self::read_field_le(ADC1_INIT_CODE_ATTEN3),
|
||||
]
|
||||
} else {
|
||||
[
|
||||
Self::read_field_le(ADC2_INIT_CODE_ATTEN0),
|
||||
Self::read_field_le(ADC2_INIT_CODE_ATTEN1),
|
||||
Self::read_field_le(ADC2_INIT_CODE_ATTEN2),
|
||||
Self::read_field_le(ADC2_INIT_CODE_ATTEN3),
|
||||
]
|
||||
};
|
||||
|
||||
// Version 1 logic for calculating ADC ICode based on EFUSE burnt value
|
||||
|
||||
let mut adc_icode = [0; 4];
|
||||
if unit == 0 {
|
||||
adc_icode[0] = adc_icode_diff[0] + 1850;
|
||||
adc_icode[1] = adc_icode_diff[1] + adc_icode[0] + 90;
|
||||
adc_icode[2] = adc_icode_diff[2] + adc_icode[1];
|
||||
adc_icode[3] = adc_icode_diff[3] + adc_icode[2] + 70;
|
||||
} else {
|
||||
adc_icode[0] = adc_icode_diff[0] + 2020;
|
||||
adc_icode[1] = adc_icode_diff[1] + adc_icode[0];
|
||||
adc_icode[2] = adc_icode_diff[2] + adc_icode[1];
|
||||
adc_icode[3] = adc_icode_diff[3] + adc_icode[2];
|
||||
}
|
||||
|
||||
Some(
|
||||
adc_icode[match atten {
|
||||
Attenuation::Attenuation0dB => 0,
|
||||
Attenuation::Attenuation2p5dB => 1,
|
||||
Attenuation::Attenuation6dB => 2,
|
||||
Attenuation::Attenuation11dB => 3,
|
||||
}],
|
||||
)
|
||||
}
|
||||
|
||||
/// Get ADC reference point voltage for specified attenuation in millivolts
|
||||
///
|
||||
/// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32s3/esp_efuse_rtc_calib.c#L63>
|
||||
pub fn get_rtc_calib_cal_mv(_unit: u8, _atten: Attenuation) -> u16 {
|
||||
850
|
||||
}
|
||||
|
||||
/// Get ADC reference point digital code for specified attenuation
|
||||
///
|
||||
/// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32s3/esp_efuse_rtc_calib.c#L63>
|
||||
pub fn get_rtc_calib_cal_code(unit: u8, atten: Attenuation) -> Option<u16> {
|
||||
let version = Self::get_rtc_calib_version();
|
||||
|
||||
if version != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let adc_vol_diff: [u16; 8] = [
|
||||
Self::read_field_le(ADC1_CAL_VOL_ATTEN0),
|
||||
Self::read_field_le(ADC1_CAL_VOL_ATTEN1),
|
||||
Self::read_field_le(ADC1_CAL_VOL_ATTEN2),
|
||||
Self::read_field_le(ADC1_CAL_VOL_ATTEN3),
|
||||
Self::read_field_le(ADC2_CAL_VOL_ATTEN0),
|
||||
Self::read_field_le(ADC2_CAL_VOL_ATTEN1),
|
||||
Self::read_field_le(ADC2_CAL_VOL_ATTEN2),
|
||||
Self::read_field_le(ADC2_CAL_VOL_ATTEN3),
|
||||
];
|
||||
|
||||
let mut adc1_vol = [0; 4];
|
||||
let mut adc2_vol = [0; 4];
|
||||
adc1_vol[3] = adc_vol_diff[3] + 900;
|
||||
adc1_vol[2] = adc_vol_diff[2] + adc1_vol[3] + 800;
|
||||
adc1_vol[1] = adc_vol_diff[1] + adc1_vol[2] + 700;
|
||||
adc1_vol[0] = adc_vol_diff[0] + adc1_vol[1] + 800;
|
||||
adc2_vol[3] = adc1_vol[3] - adc_vol_diff[7] + 15;
|
||||
adc2_vol[2] = adc1_vol[2] - adc_vol_diff[6] + 20;
|
||||
adc2_vol[1] = adc1_vol[1] - adc_vol_diff[5] + 10;
|
||||
adc2_vol[0] = adc1_vol[0] - adc_vol_diff[4] + 40;
|
||||
|
||||
let atten = match atten {
|
||||
Attenuation::Attenuation0dB => 0,
|
||||
Attenuation::Attenuation2p5dB => 1,
|
||||
Attenuation::Attenuation6dB => 2,
|
||||
Attenuation::Attenuation11dB => 3,
|
||||
};
|
||||
|
||||
Some(if unit == 0 {
|
||||
adc1_vol[atten]
|
||||
} else {
|
||||
adc2_vol[atten]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
||||
66
esp32s3-hal/examples/adc_cal.rs
Normal file
66
esp32s3-hal/examples/adc_cal.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! Connect a potentiometer to PIN3 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 esp32s3_hal::{
|
||||
adc::{self, AdcConfig, Attenuation, ADC, ADC1},
|
||||
clock::ClockControl,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
let timer_group0 = TimerGroup::new(
|
||||
peripherals.TIMG0,
|
||||
&clocks,
|
||||
&mut system.peripheral_clock_control,
|
||||
);
|
||||
let mut wdt = timer_group0.wdt;
|
||||
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
|
||||
|
||||
// Disable MWDT and RWDT (Watchdog) flash boot protection
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
// Create ADC instances
|
||||
let analog = peripherals.SENS.split();
|
||||
|
||||
let mut adc1_config = AdcConfig::new();
|
||||
|
||||
let atten = Attenuation::Attenuation11dB;
|
||||
|
||||
// You can try any of the following calibration methods by uncommenting them
|
||||
// type AdcCal = ();
|
||||
// type AdcCal = adc::AdcCalBasic<ADC1>;
|
||||
// type AdcCal = adc::AdcCalLine<ADC1>;
|
||||
type AdcCal = adc::AdcCalCurve<ADC1>;
|
||||
|
||||
let mut pin = adc1_config.enable_pin_with_cal::<_, AdcCal>(io.pins.gpio3.into_analog(), atten);
|
||||
|
||||
let mut adc1 = ADC::<ADC1>::adc(analog.adc1, adc1_config).unwrap();
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
|
||||
loop {
|
||||
let pin_value: u16 = nb::block!(adc1.read(&mut pin)).unwrap();
|
||||
let pin_value_mv = pin_value as u32 * atten.ref_mv() as u32 / 4096;
|
||||
println!("PIN2 ADC reading = {pin_value} ({pin_value_mv} mV)");
|
||||
delay.delay_ms(1500u32);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user