diff --git a/examples/adc-continious-dma.rs b/examples/adc-continious-dma.rs index 844a5c83..e7ceb824 100644 --- a/examples/adc-continious-dma.rs +++ b/examples/adc-continious-dma.rs @@ -5,6 +5,7 @@ mod utils; use crate::hal::{ adc::{ + config, config::{Continuous, Dma as AdcDma, SampleTime, Sequence}, AdcClaim, ClockSource, Temperature, Vref, }, @@ -13,13 +14,11 @@ use crate::hal::{ gpio::GpioExt, pwr::PwrExt, rcc::{Config, RccExt}, + signature::{VrefCal, VDDA_CALIB}, stm32::Peripherals, }; use stm32g4xx_hal as hal; -use stm32g4xx_hal::adc::config; -use stm32g4xx_hal::signature::{VrefCal, VDDA_CALIB}; - use cortex_m_rt::entry; use utils::logger::info; @@ -92,8 +91,11 @@ fn main() -> ! { let vref = Vref::sample_to_millivolts_ext((b[2] + b[5]) / 2, vdda, config::Resolution::Twelve); info!("vref: {}mV", vref); - let raw_temp = (((b[1] + b[4]) / 2) as f32 * (vdda as f32 / 3000.0)) as u16; - let temp = Temperature::temperature_to_degrees_centigrade(raw_temp); + let temp = Temperature::temperature_to_degrees_centigrade( + (b[1] + b[4]) / 2, + vdda as f32 / 1000., + config::Resolution::Twelve, + ); info!("temp: {}°C", temp); } } diff --git a/examples/adc-continious.rs b/examples/adc-continious.rs index 85e3f1b1..53f12e1a 100644 --- a/examples/adc-continious.rs +++ b/examples/adc-continious.rs @@ -3,13 +3,14 @@ use crate::hal::{ adc::{ - config::{Continuous, SampleTime, Sequence}, + config::{Continuous, Resolution, SampleTime, Sequence}, AdcClaim, ClockSource, Temperature, Vref, }, delay::SYSTDelayExt, gpio::GpioExt, pwr::PwrExt, rcc::{Config, RccExt}, + signature::{VrefCal, VDDA_CALIB}, stm32::Peripherals, }; use stm32g4xx_hal as hal; @@ -65,11 +66,17 @@ fn main() -> ! { info!("pa3: {}mV", millivolts); adc = adc.wait_for_conversion_sequence().unwrap_active(); - let millivolts = Vref::sample_to_millivolts(adc.current_sample()); + let vref_sample = adc.current_sample(); + let millivolts = Vref::sample_to_millivolts(vref_sample); + let vdda = VDDA_CALIB * VrefCal::get().read() as u32 / vref_sample as u32; info!("vref: {}mV", millivolts); adc = adc.wait_for_conversion_sequence().unwrap_active(); - let millivolts = Temperature::temperature_to_degrees_centigrade(adc.current_sample()); - info!("temp: {}℃C", millivolts); // Note: Temperature seems quite low... + let temp = Temperature::temperature_to_degrees_centigrade( + adc.current_sample(), + vdda as f32 / 1000., + Resolution::Twelve, + ); + info!("temp: {}°C", temp); } } diff --git a/examples/adc-one-shot-dma.rs b/examples/adc-one-shot-dma.rs index 0d50d253..898e7f02 100644 --- a/examples/adc-one-shot-dma.rs +++ b/examples/adc-one-shot-dma.rs @@ -5,7 +5,7 @@ use cortex_m_rt::entry; use crate::hal::{ adc::{ - config::{Continuous, Dma as AdcDma, SampleTime, Sequence}, + config::{Continuous, Dma as AdcDma, Resolution, SampleTime, Sequence}, AdcClaim, ClockSource, Temperature, }, delay::SYSTDelayExt, @@ -81,8 +81,11 @@ fn main() -> ! { let millivolts = adc.sample_to_millivolts(first_buffer[0]); info!("pa3: {}mV", millivolts); - let millivolts = Temperature::temperature_to_degrees_centigrade(first_buffer[1]); - info!("temp: {}℃C", millivolts); // Note: Temperature seems quite low... + + // Assume vdda is 3.3V, see adc-continious.rs for an example of measuring VDDA using VREF + let temp = + Temperature::temperature_to_degrees_centigrade(first_buffer[1], 3.3, Resolution::Twelve); + info!("temp: {}°C", temp); #[allow(clippy::empty_loop)] loop {} diff --git a/src/adc.rs b/src/adc.rs index 3519ed6d..e131aceb 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -60,12 +60,76 @@ pub struct Vbat; /// Core temperature internal signal pub struct Temperature; impl Temperature { + /// Precompute the inverse of `VTEMP_CAL_VREFANALOG`, in volts, + /// for floating point calculations + const INV_VREFANALOG_VOLTS: f32 = 1000. / VDDA_CALIB as f32; + /// Temperature at which temperature sensor has been calibrated in production + /// for data into [`VtempCal30`] (tolerance: +-5 DegC) (unit: DegC). + const VTEMP_CAL_T30: u16 = 30; + /// Temperature at which temperature sensor has been calibrated in production + /// for data into [`VtempCal130`] (tolerance: +-5 DegC) (unit: DegC). + const VTEMP_CAL_T130: u16 = 130; + + /// Convert a sample to 12 bits. Reference voltages were captured at 12 bits. + const fn to_12b(sample: u16, resolution: config::Resolution) -> u16 { + match resolution { + config::Resolution::Six => sample << 6, + config::Resolution::Eight => sample << 4, + config::Resolution::Ten => sample << 2, + config::Resolution::Twelve => sample, + } + } + + /// Convert a raw sample from `Temperature` to deg C. + /// + /// ## Arguments + /// * `sample`: ADC sample taken on the [`Temperature`] channel. + /// * `vdda`: Analog reference voltage (vref+) when the temperature + /// sample was taken, in volts. + /// * `resolution`: Configured ADC resolution. + #[inline(always)] + pub fn temperature_to_degrees_centigrade( + sample: u16, + vdda: f32, + resolution: config::Resolution, + ) -> f32 { + // Reference measurements were taken at 12 bits + let sample_12b = Self::to_12b(sample, resolution); + + // Normalize for the difference in VDDA + let sample_normalized = sample_12b as f32 * (vdda * Self::INV_VREFANALOG_VOLTS); + + ((sample_normalized - VtempCal30::get().read() as f32) + * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as f32)) + / ((VtempCal130::get().read() - VtempCal30::get().read()) as f32) + + Self::VTEMP_CAL_T30 as f32 + } + /// Convert a raw sample from `Temperature` to deg C + /// + /// ## Arguments + /// * `sample`: ADC sample taken on the [`Temperature`] channel. + /// * `vdda`: Analog reference voltage (vref+) when the temperature + /// sample was taken, in millivolts. + /// * `resolution`: Configured ADC resolution. #[inline(always)] - pub fn temperature_to_degrees_centigrade(sample: u16) -> f32 { - ((130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) - * (sample as f32 - VtempCal30::get().read() as f32)) - + 30.0 + pub fn temperature_to_degrees_centigrade_coarse( + sample: u16, + vdda: u32, + resolution: config::Resolution, + ) -> i16 { + // Reference measurements were taken at 12 bits + let sample_12b = Self::to_12b(sample, resolution); + + // Normalize for the difference in VDDA + let sample_normalized = ((sample_12b as u32 * vdda) / VDDA_CALIB) as u16; + + let t = ((sample_normalized as i32 - VtempCal30::get().read() as i32) + * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as i32)) + / ((VtempCal130::get().read() - VtempCal30::get().read()) as i32) + + Self::VTEMP_CAL_T30 as i32; + + t as i16 } }