Skip to content
Open

Adc #70

Show file tree
Hide file tree
Changes from 6 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
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:
logger:
- log
- defmt
adc:
- adc
-
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure how we want to test this... This duplicates the number of tests to run

Copy link
Member

Choose a reason for hiding this comment

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

Does the ADC need to be feature gated?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that depends on how we want to do with the extra dependencies.

env: # Peripheral Feature flags
FLAGS: rt

Expand All @@ -45,6 +48,6 @@ jobs:
- name: Install thumbv8m rust target
run: rustup target add thumbv8m.main-none-eabihf
- name: Build
run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }}
run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }}
- name: Test
run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }}
run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }}
14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,22 @@ defmt = [
"stm32h5/defmt",
]

adc = ["dep:embedded-hal-02", "dep:nb"]
Copy link
Member Author

Choose a reason for hiding this comment

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

We need embedded_hal_02 for the adc::Channel trait. However we could probably avoid nb if we provide our own blocking Adc::convert method for those that do not need to be generic using the embedded_hal_02::adc::Oneshot trait.

Copy link
Member

Choose a reason for hiding this comment

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

What's the advantage of using that trait if embedded-hal has dropped it in 1.0?


[dependencies]
cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] }
stm32h5 = { package = "stm32h5", version = "0.16.0" }
fugit = "0.3.7"
embedded-dma = "0.2"
embedded-hal = "1.0.0"
embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], optional = true }

defmt = { version = "1.0.0", optional = true }
paste = "1.0.15"
log = { version = "0.4.20", optional = true}
futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true}
stm32-usbd = "0.8.0"
nb = { version = "1.1.0", optional = true }

[dev-dependencies]
log = { version = "0.4.20"}
Expand Down Expand Up @@ -116,3 +121,12 @@ required-features = ["stm32h503"]
[[example]]
name = "i2c_target_manual_ack"
required-features = ["stm32h503"]

[[example]]
name = "adc"
required-features = ["adc"]

[[example]]
name = "adc12"
required-features = ["adc", "rm0481"]

90 changes: 90 additions & 0 deletions examples/adc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! Example of reading a voltage with ADC1
//!
//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs
#![no_main]
#![no_std]

use cortex_m_rt::entry;

use embedded_hal_02::adc::OneShot;
use stm32h5xx_hal::{
adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel,
};
use utilities::logger::info;

#[macro_use]
mod utilities;

#[entry]
fn main() -> ! {
utilities::logger::init();
let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();

// Constrain and Freeze power
info!("Setup PWR... ");
let pwr = dp.PWR.constrain();
let pwrcfg = pwr.freeze();

// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();

// We need to configure a clock for adc_ker_ck_input. The default
// adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced
// from the 64MHz HSI
//
// adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The
// maximum f_adc is 50MHz
let mut ccdr = rcc
.sys_ck(192.MHz())
.pll1_q_ck(64.MHz())
.freeze(&pwrcfg, &dp.SBS);

// Switch adc_ker_ck_input multiplexer to per_ck
ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer);

info!("");
info!("stm32h5xx-hal example - ADC");
info!("");

let mut delay = Delay::new(cp.SYST, &ccdr.clocks);

// Setup ADC
let mut adc1 = adc::Adc::new(
dp.ADC1,
4.MHz(),
&mut delay,
ccdr.peripheral.ADC,
&ccdr.clocks,
&pwrcfg,
);
Copy link
Member Author

Choose a reason for hiding this comment

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

I am not too happy with the api for setting up the adc. I think I would prefer something more like the G4 hal

let adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc);
let mut adc = adc12_common.claim_and_configure(
    dp.ADC2,
    stm32g4xx_hal::adc::config::AdcConfig::default(),
    &mut delay,
);

Here settings are divided into the individual config and the common config. Config structs are used with sensible defaults for those that do not care instead of individual arguments for frequency etc/or setter methods that have to be used after the fact

Copy link
Member

Choose a reason for hiding this comment

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

We can't use extension traits like we use for other peripherals?

Copy link
Member Author

@usbalbin usbalbin Aug 29, 2025

Choose a reason for hiding this comment

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

Probably, I just have not really changed the api from H7 yet. But yes that would probably be more consistent with the rest of the H5 hal.

The H503 only has one adc so it does not have the ADCC common register block as a separate block so that would probably not work with the above from G4


let mut temp = adc::Temperature::new();
temp.enable(&mut adc1);
let mut adc1: adc::Adc<
stm32h5::Periph<pac::adc1::RegisterBlock, 1107460096>,
adc::Enabled,
> = adc1.enable();

// We can't use ADC2 here because ccdr.peripheral.ADC12 has been
// consumed. See examples/adc12.rs

// Setup GPIOC
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);

// Configure pc0 as an analog input
let mut channel = gpioc.pc0.into_analog(); // ANALOG IN 10

loop {
let data = adc1.read(&mut channel).unwrap();
// voltage = reading * (vref/resolution)
info!(
"ADC reading: {}, voltage for nucleo: {}V. Temp reading: {}",
data,
data as f32 * (3.3 / adc1.slope() as f32),
adc1.read(&mut temp).unwrap()
);
}
}
83 changes: 83 additions & 0 deletions examples/adc12.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Example of using ADC1 and ADC2 together
//!
//! This is not available for H503 since it only has ADC1
//!
//! For an example of using ADC1 alone, see examples/adc.rs

#![no_main]
#![no_std]

use cortex_m_rt::entry;

use embedded_hal_02::adc::OneShot;
use stm32h5xx_hal::{
adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel,
};
use utilities::logger::info;

#[macro_use]
mod utilities;

#[entry]
fn main() -> ! {
utilities::logger::init();
let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();

// Constrain and Freeze power
info!("Setup PWR... ");
let pwr = dp.PWR.constrain();
let pwrcfg = pwr.freeze();

// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();

// We need to configure a clock for adc_ker_ck_input. The default
// adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced
// from the 64MHz HSI
//
// adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The
// maximum f_adc is 50MHz
let mut ccdr = rcc
.sys_ck(192.MHz())
.pll1_q_ck(64.MHz())
.freeze(&pwrcfg, &dp.SBS);

// Switch adc_ker_ck_input multiplexer to per_ck
ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer);

info!("");
info!("stm32h5xx-hal example - ADC1 and ADC2");
info!("");

let mut delay = Delay::new(cp.SYST, &ccdr.clocks);

// Setup ADC
// Setup ADC1 and ADC2
let (adc1, adc2) = adc::adc12(
dp.ADC1,
dp.ADC2,
4.MHz(),
&mut delay,
ccdr.peripheral.ADC,
&ccdr.clocks,
&pwrcfg,
);

let mut adc1 = adc1.enable();
let mut adc2 = adc2.enable();

// Setup GPIOC
// NOTE: PC2 and PC3 are only pinned out on TFBGA packages!!
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
let mut channel_pc2 = gpioc.pc2.into_analog(); // AIN 12
let mut channel_pc3 = gpioc.pc3.into_analog(); // AIN 13

loop {
let data_pc2 = adc1.read(&mut channel_pc2).unwrap();
let data_pc3 = adc2.read(&mut channel_pc3).unwrap();
// voltage = reading * (vref/resolution)
info!("ADC readings: {} {}", data_pc2, data_pc3);
}
}
2 changes: 1 addition & 1 deletion examples/blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() -> ! {

// Constrain and Freeze clock
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
let mut led = gpioa.pa5.into_push_pull_output();
Expand Down
2 changes: 1 addition & 1 deletion examples/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn main() -> ! {

// Constrain and Freeze clock
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS);

let channels = dp.GPDMA1.channels(ccdr.peripheral.GPDMA1);

Expand Down
2 changes: 1 addition & 1 deletion examples/dwt-blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn main() -> ! {
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
let mut led = gpioa.pa5.into_push_pull_output();
Expand Down
2 changes: 1 addition & 1 deletion examples/fractional-pll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn main() -> ! {
.pll2_r_ck(3_024_000.Hz())
// pll2_p / 2 --> mco2
.mco2_from_pll2_p_ck(7.MHz())
.freeze(pwrcfg, &dp.SBS);
.freeze(&pwrcfg, &dp.SBS);

// // Enable MCO2 output pin
// let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
Expand Down
2 changes: 1 addition & 1 deletion examples/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn main() -> ! {
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);

Expand Down
2 changes: 1 addition & 1 deletion examples/i2c_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn main() -> ! {
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);

Expand Down
2 changes: 1 addition & 1 deletion examples/i2c_target_manual_ack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn main() -> ! {
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);

Expand Down
2 changes: 1 addition & 1 deletion examples/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn main() -> ! {
// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS);

info!("");
info!("stm32h5xx-hal example - RCC");
Expand Down
2 changes: 1 addition & 1 deletion examples/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn main() -> ! {
let ccdr = rcc
.sys_ck(192.MHz())
.pll1_q_ck(64.MHz())
.freeze(pwrcfg, &dp.SBS);
.freeze(&pwrcfg, &dp.SBS);

// Acquire the GPIOB peripheral. This also enables the clock for
// GPIOB in the RCC register.
Expand Down
2 changes: 1 addition & 1 deletion examples/spi_send_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn main() -> ! {
let ccdr = rcc
.sys_ck(192.MHz())
.pll1_q_ck(64.MHz())
.freeze(pwrcfg, &dp.SBS);
.freeze(&pwrcfg, &dp.SBS);

// Acquire the GPIOB peripheral. This also enables the clock for
// GPIOB in the RCC register.
Expand Down
2 changes: 1 addition & 1 deletion examples/spi_slave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn main() -> ! {
let ccdr = rcc
.sys_ck(100.MHz())
.pll1_q_ck(50.MHz())
.freeze(pwrcfg, &dp.SBS);
.freeze(&pwrcfg, &dp.SBS);

// Acquire the GPIOB peripheral. This also enables the clock for
// GPIOB in the RCC register.
Expand Down
2 changes: 1 addition & 1 deletion examples/usb_serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn main() -> ! {
let pwrcfg = pwr.vos0().freeze();
// Constrain and Freeze clock
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS);

let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);

Expand Down
Loading
Loading