Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vintref cal #128

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/supply-monitor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Supply Monitor

## Measurement

Measurement done at Coredump with `psu-rn` and `UT61E` multimeter.

VDDA was measured to be 3.28V at Vbat = 4.51V

|Vbat|VREF Raw|VDDA|Vin raw|Vin new|Vin old|
|----|--------|----|-------|-------|-------|
|5.00| 1580 |3.15| 3850 | 4.89 | 5.12 |
|4.51| 1580 |3.15| 3476 | 4.41 | 4.62 |
|4.01| 1580 |3.15| 3104 | 3.94 | 4.12 |
|3.50| 1580 |3.15| 2716 | 3.45 | 3.61 |
|3.30| 1580 |3.15| 2568 | 3.25 | 3.42 |
|3.00| 1736 |2.87| 2563 | 2.97 | 3.41 |
|2.60| 2006 |2.49| 2578 | 2.58 | 3.43 |

The new methods seems to calculate VDDA slightly wrong and thus calculates a
too low value for Vin. But the error seems to be similar than with the old
measurement method.
3 changes: 2 additions & 1 deletion firmware/.embed.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ chip = "STM32L071KBTx"

[default.probe]
protocol = "Swd"
speed = 2500
#speed = 2500
#speed = 2000

# Disable everything for default command

Expand Down
1 change: 1 addition & 0 deletions firmware/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions firmware/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ gfroerli-common = { path = "../common" }

bitfield = "0.13"
cortex-m = "0.7" # Keep in sync with stm32l0xx-hal
cortex-m-rt = "0.6.15"
cortex-m-rtic = "1.0.0"
embedded-hal = { version = "0.2.3", features = ["unproven"] } # Keep in sync with stm32l0xx-hal
embedded-time = "0.12" # Keep in sync with stm32l0xx-hal
Expand Down
108 changes: 108 additions & 0 deletions firmware/examples/supply_monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Prints the supply voltage monitor output to the serial port.
#![no_main]
#![no_std]

use panic_persist as _;

use core::fmt::Write;

use cortex_m_rt::entry;
use embedded_time::rate::Baud;
use hal::serial;
use stm32l0xx_hal as hal;
use stm32l0xx_hal::prelude::*;

use gfroerli_firmware::supply_monitor;

#[entry]
fn main() -> ! {
let p = cortex_m::Peripherals::take().unwrap();
let dp = stm32l0xx_hal::pac::Peripherals::take().unwrap();

let syst = p.SYST;
let mut rcc = dp.RCC.freeze(hal::rcc::Config::hsi16());
let mut delay = hal::delay::Delay::new(syst, rcc.clocks);

let gpiob = dp.GPIOB.split(&mut rcc);
let mut led = gpiob.pb3.into_push_pull_output();

let gpioa = dp.GPIOA.split(&mut rcc);

/*
// Nucleo serial
let mut serial = hal::serial::Serial::usart2(
dp.USART2,
gpioa.pa2.into_floating_input(),
gpioa.pa3.into_floating_input(),
hal::serial::Config {
baudrate: Baud(9600),
wordlength: hal::serial::WordLength::DataBits8,
parity: hal::serial::Parity::ParityNone,
stopbits: hal::serial::StopBits::STOP1,
},
&mut rcc,
)
.unwrap();
*/

let mut serial = hal::serial::Serial::usart1(
dp.USART1,
gpiob.pb6.into_floating_input(),
gpiob.pb7.into_floating_input(),
serial::Config {
baudrate: Baud(57_600),
wordlength: serial::WordLength::DataBits8,
parity: serial::Parity::ParityNone,
stopbits: serial::StopBits::STOP1,
},
&mut rcc,
)
.unwrap();

writeln!(serial, "Starting supply_monitor example").unwrap();

// Initialize supply monitor
let adc = dp.ADC.constrain(&mut rcc);

let a1 = gpioa.pa1.into_analog();
let adc_enable_pin = gpioa.pa5.into_push_pull_output().downgrade();
let mut supply_monitor = supply_monitor::SupplyMonitor::new(a1, adc, adc_enable_pin);

loop {
supply_monitor.enable();
if let Some(vref_raw) = supply_monitor.read_vref_raw() {
let v_input = (vref_raw as f32) / 4095.0 * 3.3;
let v_dda = supply_monitor::SupplyMonitor::convert_vrefint_to_vdda(vref_raw);

writeln!(
serial,
"VREF: {} ({}) -> VDDA: {}",
vref_raw, v_input, v_dda
)
.unwrap();

let v_supply = supply_monitor.read_supply_raw();
if let Some(v_supply_raw) = v_supply {
// real ca. [email protected]
let v_input = (v_supply_raw as f32) / 4095.0 * 3.3;
let v_supply_converted_old =
supply_monitor::SupplyMonitor::convert_input(v_supply_raw, 3.3);
let v_supply_converted =
supply_monitor::SupplyMonitor::convert_input(v_supply_raw, v_dda);

writeln!(
serial,
"Raw: {} ({}) -> Supply: {} ({} old)",
v_supply_raw, v_input, v_supply_converted, v_supply_converted_old
)
.unwrap();
}
}
//supply_monitor.disable();

led.set_high().unwrap();
delay.delay_ms(1_000u16);
led.set_low().unwrap();
delay.delay_ms(1_000u16);
}
}
2 changes: 1 addition & 1 deletion firmware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ mod app {
if let Some(v_supply_f32) = v_supply
.as_ref()
.map(U12::as_u16)
.map(SupplyMonitor::convert_input)
.map(|v| SupplyMonitor::convert_input(v, 3.3))
{
delimit!();
write!(ctx.shared.debug, "VDD: {:.3}V", v_supply_f32).unwrap();
Expand Down
56 changes: 40 additions & 16 deletions firmware/src/supply_monitor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use embedded_hal::{adc::OneShot, digital::v2::OutputPin};
use stm32l0xx_hal::{
adc::{self, Adc, Align},
adc::{self, Adc, Align, VRef},
calibration::VrefintCal,
gpio::{gpioa::PA1, Analog, Output, Pin, PushPull},
};

Expand All @@ -14,42 +15,66 @@ pub struct SupplyMonitor {
}

impl SupplyMonitor {
const ADC_MAX: f32 = 4095.0;
/// Voltage at which VREFINT_CAL got calibrated
const VREFINT_CAL_VDD: f32 = 3.0;

pub fn new(
adc_pin: PA1<Analog>,
mut adc: Adc<adc::Ready>,
enable_pin: Pin<Output<PushPull>>,
) -> Self {
adc.set_precision(adc::Precision::B_12);
adc.set_align(Align::Right); // Use 12 least-significant bits to encode data
adc.set_sample_time(adc::SampleTime::T_79_5);
//adc.set_sample_time(adc::SampleTime::T_79_5);
adc.set_sample_time(adc::SampleTime::T_160_5);
SupplyMonitor {
adc_pin,
adc,
enable_pin,
}
}

/// Disable the supply voltage monitoring voltage divider
fn disable(&mut self) {
/// Disable the supply voltage monitoring voltage divider and VREFINT
pub fn disable(&mut self) {
self.enable_pin.set_low().unwrap();
VRef.disable(&mut self.adc);
}

/// Enable the supply voltage monitoring voltage divider
fn enable(&mut self) {
/// Enable the supply voltage monitoring voltage divider and VREFINT
pub fn enable(&mut self) {
self.enable_pin.set_high().unwrap();
VRef.enable(&mut self.adc);
}

/// Read the supply voltage ADC channel.
///
/// `enable` and `disable` the supply voltage monitoring voltage divider
/// before and after the measurement.
/// Make sure to call `enable` and wait 3ms before the measurement.
pub fn read_supply_raw(&mut self) -> Option<u16> {
self.enable();
let val: Option<u16> = self.adc.read(&mut self.adc_pin).ok();
self.disable();
self.adc.read(&mut self.adc_pin).ok()
}

/// Read the VREFINT value
pub fn read_vref_raw(&mut self) -> Option<u16> {
let val = self.adc.read(&mut VRef).ok();
val
}

/// Calcultate the VREFINT voltage from the calibrated value which was calibrated at 3.0V VDDA
pub fn calculate_vref_int_voltage() -> f32 {
Self::VREFINT_CAL_VDD / Self::ADC_MAX * (VrefintCal::get().read() as f32)
}

/// Convert the raw VREFINT ADC value to VDDA
pub fn convert_vrefint_to_vdda(vrefint: u16) -> f32 {
Self::VREFINT_CAL_VDD * (VrefintCal::get().read() as f32) / (vrefint as f32)
}

/// Read VREFINT and calculate VDDA from it
pub fn read_vdda(&mut self) -> Option<f32> {
self.read_vref_raw().map(Self::convert_vrefint_to_vdda)
}

/// Read the supply voltage (see `read_supply_raw` for details) and return
/// the raw data as `U12`.
pub fn read_supply_raw_u12(&mut self) -> Option<U12> {
Expand All @@ -60,16 +85,15 @@ impl SupplyMonitor {
/// the voltage in volts as `f32`.
pub fn read_supply(&mut self) -> Option<f32> {
let val = self.read_supply_raw()?;
Some(Self::convert_input(val))
let vdda = self.read_vdda()?;
Some(Self::convert_input(val, vdda))
}

/// Convert the raw ADC value to the resulting supply voltage
pub fn convert_input(input: u16) -> f32 {
const SUPPLY_VOLTAGE: f32 = 3.3;
const ADC_MAX: f32 = 4095.0;
pub fn convert_input(input: u16, vdda: f32) -> f32 {
const R_1: f32 = 9.31;
const R_2: f32 = 6.04;
(input as f32) / ADC_MAX * SUPPLY_VOLTAGE / R_1 * (R_1 + R_2)
(input as f32) / Self::ADC_MAX * vdda / R_1 * (R_1 + R_2)
}
}

Expand Down