2022-03-19 11:05:00 +01:00
use embedded_hal_02 ::blocking ::delay ::DelayUs ;
2023-08-20 19:31:47 -07:00
#[ allow(unused) ]
2022-03-19 11:05:00 +01:00
use pac ::adc ::vals ::{ Adcaldif , Boost , Difsel , Exten , Pcsel } ;
use pac ::adccommon ::vals ::Presc ;
2022-10-26 17:51:12 -05:00
use super ::{ Adc , AdcPin , Instance , InternalChannel , Resolution , SampleTime } ;
2022-07-10 17:36:10 -05:00
use crate ::time ::Hertz ;
2022-07-23 14:00:19 +02:00
use crate ::{ pac , Peripheral } ;
2022-03-19 11:05:00 +01:00
2022-07-27 01:17:26 +03:00
/// Default VREF voltage used for sample conversion to millivolts.
pub const VREF_DEFAULT_MV : u32 = 3300 ;
/// VREF voltage used for factory calibration of VREFINTCAL register.
pub const VREF_CALIB_MV : u32 = 3300 ;
2023-08-20 19:31:47 -07:00
/// Max single ADC operation clock frequency
#[ cfg(stm32g4) ]
const MAX_ADC_CLK_FREQ : Hertz = Hertz ::mhz ( 60 ) ;
#[ cfg(stm32h7) ]
const MAX_ADC_CLK_FREQ : Hertz = Hertz ::mhz ( 50 ) ;
#[ cfg(stm32g4) ]
const VREF_CHANNEL : u8 = 18 ;
#[ cfg(stm32g4) ]
const TEMP_CHANNEL : u8 = 16 ;
#[ cfg(stm32h7) ]
const VREF_CHANNEL : u8 = 19 ;
#[ cfg(stm32h7) ]
const TEMP_CHANNEL : u8 = 18 ;
// TODO this should be 14 for H7a/b/35
const VBAT_CHANNEL : u8 = 17 ;
// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
2022-07-27 01:17:26 +03:00
pub struct VrefInt ;
impl < T : Instance > InternalChannel < T > for VrefInt { }
2022-10-07 14:53:03 +03:00
impl < T : Instance > super ::sealed ::InternalChannel < T > for VrefInt {
2022-03-19 11:05:00 +01:00
fn channel ( & self ) -> u8 {
2023-08-20 19:31:47 -07:00
VREF_CHANNEL
2022-03-19 11:05:00 +01:00
}
}
pub struct Temperature ;
impl < T : Instance > InternalChannel < T > for Temperature { }
2022-10-07 14:53:03 +03:00
impl < T : Instance > super ::sealed ::InternalChannel < T > for Temperature {
2022-03-19 11:05:00 +01:00
fn channel ( & self ) -> u8 {
2023-08-20 19:31:47 -07:00
TEMP_CHANNEL
2022-03-19 11:05:00 +01:00
}
}
pub struct Vbat ;
impl < T : Instance > InternalChannel < T > for Vbat { }
2022-10-07 14:53:03 +03:00
impl < T : Instance > super ::sealed ::InternalChannel < T > for Vbat {
2022-03-19 11:05:00 +01:00
fn channel ( & self ) -> u8 {
2023-08-20 19:31:47 -07:00
VBAT_CHANNEL
2022-03-19 11:05:00 +01:00
}
}
// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
#[ allow(unused) ]
enum Prescaler {
NotDivided ,
DividedBy2 ,
DividedBy4 ,
DividedBy6 ,
DividedBy8 ,
DividedBy10 ,
DividedBy12 ,
DividedBy16 ,
DividedBy32 ,
DividedBy64 ,
DividedBy128 ,
DividedBy256 ,
}
impl Prescaler {
fn from_ker_ck ( frequency : Hertz ) -> Self {
2023-08-20 19:31:47 -07:00
let raw_prescaler = frequency . 0 / MAX_ADC_CLK_FREQ . 0 ;
2022-03-19 11:05:00 +01:00
match raw_prescaler {
0 = > Self ::NotDivided ,
1 = > Self ::DividedBy2 ,
2 ..= 3 = > Self ::DividedBy4 ,
4 ..= 5 = > Self ::DividedBy6 ,
6 ..= 7 = > Self ::DividedBy8 ,
8 ..= 9 = > Self ::DividedBy10 ,
10 ..= 11 = > Self ::DividedBy12 ,
_ = > unimplemented! ( ) ,
}
}
fn divisor ( & self ) -> u32 {
match self {
Prescaler ::NotDivided = > 1 ,
Prescaler ::DividedBy2 = > 2 ,
Prescaler ::DividedBy4 = > 4 ,
Prescaler ::DividedBy6 = > 6 ,
Prescaler ::DividedBy8 = > 8 ,
Prescaler ::DividedBy10 = > 10 ,
Prescaler ::DividedBy12 = > 12 ,
Prescaler ::DividedBy16 = > 16 ,
Prescaler ::DividedBy32 = > 32 ,
Prescaler ::DividedBy64 = > 64 ,
Prescaler ::DividedBy128 = > 128 ,
Prescaler ::DividedBy256 = > 256 ,
}
}
fn presc ( & self ) -> Presc {
match self {
Prescaler ::NotDivided = > Presc ::DIV1 ,
Prescaler ::DividedBy2 = > Presc ::DIV2 ,
Prescaler ::DividedBy4 = > Presc ::DIV4 ,
Prescaler ::DividedBy6 = > Presc ::DIV6 ,
Prescaler ::DividedBy8 = > Presc ::DIV8 ,
Prescaler ::DividedBy10 = > Presc ::DIV10 ,
Prescaler ::DividedBy12 = > Presc ::DIV12 ,
Prescaler ::DividedBy16 = > Presc ::DIV16 ,
Prescaler ::DividedBy32 = > Presc ::DIV32 ,
Prescaler ::DividedBy64 = > Presc ::DIV64 ,
Prescaler ::DividedBy128 = > Presc ::DIV128 ,
Prescaler ::DividedBy256 = > Presc ::DIV256 ,
}
}
}
2022-10-26 17:59:44 -05:00
impl < ' d , T : Instance > Adc < ' d , T > {
2022-10-26 18:36:04 -05:00
pub fn new ( adc : impl Peripheral < P = T > + ' d , delay : & mut impl DelayUs < u16 > ) -> Self {
2023-07-28 13:23:22 +02:00
embassy_hal_internal ::into_ref! ( adc ) ;
2022-03-19 11:05:00 +01:00
T ::enable ( ) ;
T ::reset ( ) ;
let prescaler = Prescaler ::from_ker_ck ( T ::frequency ( ) ) ;
2023-06-19 03:07:26 +02:00
T ::common_regs ( ) . ccr ( ) . modify ( | w | w . set_presc ( prescaler . presc ( ) ) ) ;
2022-03-19 11:05:00 +01:00
let frequency = Hertz ( T ::frequency ( ) . 0 / prescaler . divisor ( ) ) ;
2022-05-02 15:14:49 -05:00
info! ( " ADC frequency set to {} Hz " , frequency . 0 ) ;
2022-03-19 11:05:00 +01:00
2023-08-20 19:31:47 -07:00
if frequency > MAX_ADC_CLK_FREQ {
panic! ( " Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information. " , MAX_ADC_CLK_FREQ . 0 / 1_000_000 ) ;
2022-03-19 11:05:00 +01:00
}
2023-08-20 19:31:47 -07:00
#[ cfg(stm32h7) ]
{
let boost = if frequency < Hertz ::khz ( 6_250 ) {
Boost ::LT6_25
} else if frequency < Hertz ::khz ( 12_500 ) {
Boost ::LT12_5
} else if frequency < Hertz ::mhz ( 25 ) {
Boost ::LT25
} else {
Boost ::LT50
} ;
T ::regs ( ) . cr ( ) . modify ( | w | w . set_boost ( boost ) ) ;
}
2022-03-19 11:05:00 +01:00
let mut s = Self {
2022-10-26 18:36:04 -05:00
adc ,
2022-03-19 11:05:00 +01:00
sample_time : Default ::default ( ) ,
} ;
s . power_up ( delay ) ;
s . configure_differential_inputs ( ) ;
s . calibrate ( ) ;
delay . delay_us ( 1 ) ;
s . enable ( ) ;
s . configure ( ) ;
s
}
fn power_up ( & mut self , delay : & mut impl DelayUs < u16 > ) {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . cr ( ) . modify ( | reg | {
reg . set_deeppwd ( false ) ;
reg . set_advregen ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
delay . delay_us ( 10 ) ;
}
fn configure_differential_inputs ( & mut self ) {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . difsel ( ) . modify ( | w | {
for n in 0 .. 20 {
w . set_difsel ( n , Difsel ::SINGLEENDED ) ;
}
} ) ;
2022-03-19 11:05:00 +01:00
}
fn calibrate ( & mut self ) {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . cr ( ) . modify ( | w | {
w . set_adcaldif ( Adcaldif ::SINGLEENDED ) ;
w . set_adcallin ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
2023-06-19 03:07:26 +02:00
T ::regs ( ) . cr ( ) . modify ( | w | w . set_adcal ( true ) ) ;
2022-03-19 11:05:00 +01:00
2023-06-19 03:07:26 +02:00
while T ::regs ( ) . cr ( ) . read ( ) . adcal ( ) { }
2022-03-19 11:05:00 +01:00
}
fn enable ( & mut self ) {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . isr ( ) . write ( | w | w . set_adrdy ( true ) ) ;
T ::regs ( ) . cr ( ) . modify ( | w | w . set_aden ( true ) ) ;
while ! T ::regs ( ) . isr ( ) . read ( ) . adrdy ( ) { }
T ::regs ( ) . isr ( ) . write ( | w | w . set_adrdy ( true ) ) ;
2022-03-19 11:05:00 +01:00
}
fn configure ( & mut self ) {
// single conversion mode, software trigger
2023-06-19 03:07:26 +02:00
T ::regs ( ) . cfgr ( ) . modify ( | w | {
w . set_cont ( false ) ;
w . set_exten ( Exten ::DISABLED ) ;
} ) ;
2022-03-19 11:05:00 +01:00
}
2022-07-27 01:17:26 +03:00
pub fn enable_vrefint ( & self ) -> VrefInt {
2023-06-19 03:07:26 +02:00
T ::common_regs ( ) . ccr ( ) . modify ( | reg | {
reg . set_vrefen ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
2022-07-27 01:17:26 +03:00
VrefInt { }
2022-03-19 11:05:00 +01:00
}
pub fn enable_temperature ( & self ) -> Temperature {
2023-06-19 03:07:26 +02:00
T ::common_regs ( ) . ccr ( ) . modify ( | reg | {
reg . set_vsenseen ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
Temperature { }
}
pub fn enable_vbat ( & self ) -> Vbat {
2023-06-19 03:07:26 +02:00
T ::common_regs ( ) . ccr ( ) . modify ( | reg | {
reg . set_vbaten ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
Vbat { }
}
pub fn set_sample_time ( & mut self , sample_time : SampleTime ) {
self . sample_time = sample_time ;
}
pub fn set_resolution ( & mut self , resolution : Resolution ) {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . cfgr ( ) . modify ( | reg | reg . set_res ( resolution . into ( ) ) ) ;
2022-03-19 11:05:00 +01:00
}
/// Perform a single conversion.
fn convert ( & mut self ) -> u16 {
2023-06-19 03:07:26 +02:00
T ::regs ( ) . isr ( ) . modify ( | reg | {
reg . set_eos ( true ) ;
reg . set_eoc ( true ) ;
} ) ;
2022-03-19 11:05:00 +01:00
2023-06-19 03:07:26 +02:00
// Start conversion
T ::regs ( ) . cr ( ) . modify ( | reg | {
reg . set_adstart ( true ) ;
} ) ;
while ! T ::regs ( ) . isr ( ) . read ( ) . eos ( ) {
// spin
2022-03-19 11:05:00 +01:00
}
2023-06-19 03:07:26 +02:00
T ::regs ( ) . dr ( ) . read ( ) . 0 as u16
2022-03-19 11:05:00 +01:00
}
pub fn read < P > ( & mut self , pin : & mut P ) -> u16
where
P : AdcPin < T > ,
P : crate ::gpio ::sealed ::Pin ,
{
2023-06-19 03:07:26 +02:00
pin . set_as_analog ( ) ;
2022-03-19 11:05:00 +01:00
2023-06-19 03:07:26 +02:00
self . read_channel ( pin . channel ( ) )
2022-03-19 11:05:00 +01:00
}
pub fn read_internal ( & mut self , channel : & mut impl InternalChannel < T > ) -> u16 {
2023-06-19 03:07:26 +02:00
self . read_channel ( channel . channel ( ) )
2022-03-19 11:05:00 +01:00
}
2023-06-19 03:07:26 +02:00
fn read_channel ( & mut self , channel : u8 ) -> u16 {
2022-03-19 11:05:00 +01:00
// Configure channel
Self ::set_channel_sample_time ( channel , self . sample_time ) ;
2023-08-20 19:31:47 -07:00
#[ cfg(stm32h7) ]
{
T ::regs ( ) . cfgr2 ( ) . modify ( | w | w . set_lshift ( 0 ) ) ;
T ::regs ( )
. pcsel ( )
. write ( | w | w . set_pcsel ( channel as _ , Pcsel ::PRESELECTED ) ) ;
}
2022-03-19 11:05:00 +01:00
T ::regs ( ) . sqr1 ( ) . write ( | reg | {
reg . set_sq ( 0 , channel ) ;
reg . set_l ( 0 ) ;
} ) ;
self . convert ( )
}
2023-06-19 03:07:26 +02:00
fn set_channel_sample_time ( ch : u8 , sample_time : SampleTime ) {
2022-10-26 02:28:39 -05:00
let sample_time = sample_time . into ( ) ;
2022-03-19 11:05:00 +01:00
if ch < = 9 {
2022-10-26 02:28:39 -05:00
T ::regs ( ) . smpr ( 0 ) . modify ( | reg | reg . set_smp ( ch as _ , sample_time ) ) ;
2022-03-19 11:05:00 +01:00
} else {
2022-10-26 02:28:39 -05:00
T ::regs ( ) . smpr ( 1 ) . modify ( | reg | reg . set_smp ( ( ch - 10 ) as _ , sample_time ) ) ;
2022-03-19 11:05:00 +01:00
}
}
}