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

WIP: Voltage Monitor improvements on Nucleo #112

Draft
wants to merge 6 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
3 changes: 2 additions & 1 deletion firmware/.embed.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Chip and probe config

[default.general]
chip = "STM32L071KBTx"
# chip = "STM32L071KBTx"
chip = "STM32L031K6Tx"

[default.probe]
protocol = "Swd"
Expand Down
3 changes: 2 additions & 1 deletion firmware/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ embedded-hal = { version = "0.2.2", features = ["unproven"] }
one-wire-bus = "0.1.1"
rn2xx3 = "0.2.1"
stm32l0 = "0.10.0"
stm32l0xx-hal = { git = "ssh://[email protected]/stm32-rs/stm32l0xx-hal.git", branch = "master", features = ["rt", "mcu-STM32L071KBTx"] }
#stm32l0xx-hal = { git = "ssh://[email protected]/stm32-rs/stm32l0xx-hal.git", branch = "master", features = ["rt", "mcu-STM32L071KBTx"] }
stm32l0xx-hal = { git = "ssh://[email protected]/stm32-rs/stm32l0xx-hal.git", branch = "master", features = ["rt", "mcu-STM32L031K6Tx"] }
shtcx = { git = "https://github.com/dbrgn/shtcx-rs", branch = "master" }

[target.'cfg(target_arch = "arm")'.dependencies]
Expand Down
86 changes: 86 additions & 0 deletions firmware/examples/supply_monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! 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 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);
let mut serial = hal::serial::Serial::usart2(
dp.USART2,
gpioa.pa2.into_floating_input(),
gpioa.pa3.into_floating_input(),
//gpioa.pa9.into_floating_input(),
//gpioa.pa10.into_floating_input(),

//gpiob.pb6.into_floating_input(),
//gpiob.pb7.into_floating_input(),
hal::serial::Config {
baudrate: hal::time::Bps(9600),
wordlength: hal::serial::WordLength::DataBits8,
parity: hal::serial::Parity::ParityNone,
stopbits: hal::serial::StopBits::STOP1,
},
&mut rcc,
)
.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 {
let v_supply = supply_monitor.read_supply_raw();
if let Some(v) = v_supply {
writeln!(serial, "{}", v);

// real ca. [email protected]
let v_input = (v as f32) / 4095.0 * 3.3;
let v_supply_converted = (v as f32) / 4095.0 * 3.3 / 2.7 * (10.0 + 2.7);

writeln!(serial, "{} -> {}", v_input, v_supply_converted).unwrap();
}

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

writeln!(serial, "VDDA: {} ({}) -> {}", v, v_input, v_dda).unwrap();
}
*/

if let Some(vdd) = supply_monitor.read_supply() {
writeln!(serial, "VDD: {:?}", vdd);
}

//serial.write_char('a').unwrap();

led.set_high().unwrap();
delay.delay(hal::time::MicroSeconds(1_000_000));
led.set_low().unwrap();
delay.delay(hal::time::MicroSeconds(1_000_000));
}
}
4 changes: 2 additions & 2 deletions firmware/memory.x
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
MEMORY
{
/* NOTE K = KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 128K
RAM : ORIGIN = 0x20000000, LENGTH = 19K
FLASH : ORIGIN = 0x08000000, LENGTH = 32K
RAM : ORIGIN = 0x20000000, LENGTH = 8K
PANDUMP : ORIGIN = 0x20004C00, LENGTH = 1K
}

Expand Down
46 changes: 38 additions & 8 deletions firmware/src/supply_monitor.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use embedded_hal::adc::OneShot;
use embedded_hal::digital::v2::OutputPin;
use stm32l0xx_hal::adc::{self, Adc, Align};
use stm32l0xx_hal::adc::{self, Adc, Align, VRef};
use stm32l0xx_hal::gpio::gpioa::{PA, PA1};
use stm32l0xx_hal::gpio::{Analog, Output, PushPull};
use stm32l0xx_hal::calibration::VrefintCal;

use gfroerli_common::measurement::U12;

Expand All @@ -14,6 +15,9 @@ pub struct SupplyMonitor {
}

impl SupplyMonitor {
const ADC_MAX: f32 = 4095.0;
const VREFINT_VOLTAGE: f32 = 1.224;

pub fn new(
adc_pin: PA1<Analog>,
mut adc: Adc<adc::Ready>,
Expand Down Expand Up @@ -50,6 +54,33 @@ impl SupplyMonitor {
val
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should wait around 3ms between the enable and the read. But not much longer to avoid power usage. Should we do the API asynchronously?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'd split enabling and reading into separate methods.

Does VRef draw significant power as well? If yes, we should probably have a dedicated task that reads the data after 3ms and turns off VRef. If not, we can collect the measurement together with the other sensor data.

VRef.disable(&mut self.adc);
val
}

/// Calcultate the VREFINT voltage from the calibrated value which was calibrated at 3.0V VDDA
pub fn calculate_vref_int_voltage() -> f32 {
3.0 / 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 {
let vrefint_voltage = Self::calculate_vref_int_voltage();
let vdda = Self::ADC_MAX * vrefint_voltage / (vrefint as f32);


let vdda = 3.0 * (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 +91,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;
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)
pub fn convert_input(input: u16, vdda: f32) -> f32 {
const R_1: f32 = 2.7;
const R_2: f32 = 10.0;
(input as f32) / Self::ADC_MAX * vdda / R_1 * (R_1 + R_2)
}
}

Expand Down