diff options
| author | Matthew W. Samsonoff <[email protected]> | 2022-09-13 23:08:03 -0400 |
|---|---|---|
| committer | Grant Miller <[email protected]> | 2023-04-05 14:34:24 -0500 |
| commit | 5d9ae3dbdbfe8ba6e1008cd2eadc09743cfc6284 (patch) | |
| tree | 7c3be1449c664af291cb7eebcaf5372d10745f5e | |
| parent | eed2b123253380d67f76bf1d0272688e8053bc9a (diff) | |
Add implementation of STM32 v1 ADC
| -rw-r--r-- | embassy-stm32/src/adc/v1.rs | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 8b1378917..923a1d97a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -1 +1,278 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 1 | 2 | ||
| 3 | use embassy_hal_common::into_ref; | ||
| 4 | use embedded_hal_02::blocking::delay::DelayUs; | ||
| 5 | |||
| 6 | use crate::adc::{AdcPin, Instance}; | ||
| 7 | use crate::{pac, Peripheral}; | ||
| 8 | |||
| 9 | pub const VDDA_CALIB_MV: u32 = 3300; | ||
| 10 | pub const VREF_INT: u32 = 1230; | ||
| 11 | |||
| 12 | fn enable() { | ||
| 13 | critical_section::with(|_| unsafe { | ||
| 14 | crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); | ||
| 15 | }); | ||
| 16 | } | ||
| 17 | |||
| 18 | pub enum Resolution { | ||
| 19 | TwelveBit, | ||
| 20 | TenBit, | ||
| 21 | EightBit, | ||
| 22 | SixBit, | ||
| 23 | } | ||
| 24 | |||
| 25 | impl Default for Resolution { | ||
| 26 | fn default() -> Self { | ||
| 27 | Self::TwelveBit | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | impl Resolution { | ||
| 32 | fn res(&self) -> pac::adc::vals::Res { | ||
| 33 | match self { | ||
| 34 | Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, | ||
| 35 | Resolution::TenBit => pac::adc::vals::Res::TENBIT, | ||
| 36 | Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, | ||
| 37 | Resolution::SixBit => pac::adc::vals::Res::SIXBIT, | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn to_max_count(&self) -> u32 { | ||
| 42 | match self { | ||
| 43 | Resolution::TwelveBit => (1 << 12) - 1, | ||
| 44 | Resolution::TenBit => (1 << 10) - 1, | ||
| 45 | Resolution::EightBit => (1 << 8) - 1, | ||
| 46 | Resolution::SixBit => (1 << 6) - 1, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | pub struct Vbat; | ||
| 52 | impl<T: Instance> AdcPin<T> for Vbat {} | ||
| 53 | impl<T: Instance> super::sealed::AdcPin<T> for Vbat { | ||
| 54 | fn channel(&self) -> u8 { | ||
| 55 | 18 | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | pub struct Vref; | ||
| 60 | impl<T: Instance> AdcPin<T> for Vref {} | ||
| 61 | impl<T: Instance> super::sealed::AdcPin<T> for Vref { | ||
| 62 | fn channel(&self) -> u8 { | ||
| 63 | 17 | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub struct Temperature; | ||
| 68 | impl<T: Instance> AdcPin<T> for Temperature {} | ||
| 69 | impl<T: Instance> super::sealed::AdcPin<T> for Temperature { | ||
| 70 | fn channel(&self) -> u8 { | ||
| 71 | 16 | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | mod sample_time { | ||
| 76 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] | ||
| 77 | pub enum SampleTime { | ||
| 78 | /// 1.5 ADC clock cycles | ||
| 79 | Cycles1_5 = 0b000, | ||
| 80 | |||
| 81 | /// 7.5 ADC clock cycles | ||
| 82 | Cycles7_5 = 0b001, | ||
| 83 | |||
| 84 | /// 13.5 ADC clock cycles | ||
| 85 | Cycles13_5 = 0b010, | ||
| 86 | |||
| 87 | /// 28.5 ADC clock cycles | ||
| 88 | Cycles28_5 = 0b011, | ||
| 89 | |||
| 90 | /// 41.5 ADC clock cycles | ||
| 91 | Cycles41_5 = 0b100, | ||
| 92 | |||
| 93 | /// 55.5 ADC clock cycles | ||
| 94 | Cycles55_5 = 0b101, | ||
| 95 | |||
| 96 | /// 71.5 ADC clock cycles | ||
| 97 | Cycles71_5 = 0b110, | ||
| 98 | |||
| 99 | /// 239.5 ADC clock cycles | ||
| 100 | Cycles239_5 = 0b111, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl SampleTime { | ||
| 104 | pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { | ||
| 105 | match self { | ||
| 106 | SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, | ||
| 107 | SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, | ||
| 108 | SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, | ||
| 109 | SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, | ||
| 110 | SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, | ||
| 111 | SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, | ||
| 112 | SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, | ||
| 113 | SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl Default for SampleTime { | ||
| 119 | fn default() -> Self { | ||
| 120 | Self::Cycles1_5 | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | pub use sample_time::SampleTime; | ||
| 126 | |||
| 127 | pub struct Adc<'d, T: Instance> { | ||
| 128 | sample_time: SampleTime, | ||
| 129 | vref_mv: u32, | ||
| 130 | resolution: Resolution, | ||
| 131 | phantom: PhantomData<&'d mut T>, | ||
| 132 | } | ||
| 133 | |||
| 134 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 135 | pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { | ||
| 136 | into_ref!(_peri); | ||
| 137 | enable(); | ||
| 138 | |||
| 139 | // Delay 1μs when using HSI14 as the ADC clock. | ||
| 140 | // | ||
| 141 | // Table 57. ADC characteristics | ||
| 142 | // tstab = 14 * 1/fadc | ||
| 143 | delay.delay_us(1); | ||
| 144 | |||
| 145 | let s = Self { | ||
| 146 | sample_time: Default::default(), | ||
| 147 | vref_mv: VDDA_CALIB_MV, | ||
| 148 | resolution: Resolution::default(), | ||
| 149 | phantom: PhantomData, | ||
| 150 | }; | ||
| 151 | s.calibrate(); | ||
| 152 | s | ||
| 153 | } | ||
| 154 | |||
| 155 | pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat { | ||
| 156 | // SMP must be ≥ 56 ADC clock cycles when using HSI14. | ||
| 157 | // | ||
| 158 | // 6.3.20 Vbat monitoring characteristics | ||
| 159 | // ts_vbat ≥ 4μs | ||
| 160 | unsafe { | ||
| 161 | T::regs().ccr().modify(|reg| reg.set_vbaten(true)); | ||
| 162 | } | ||
| 163 | Vbat | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn enable_vref(&self, delay: &mut impl DelayUs<u32>) -> Vref { | ||
| 167 | // Table 28. Embedded internal reference voltage | ||
| 168 | // tstart = 10μs | ||
| 169 | unsafe { | ||
| 170 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); | ||
| 171 | } | ||
| 172 | delay.delay_us(10); | ||
| 173 | Vref | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn enable_temperature(&self, delay: &mut impl DelayUs<u32>) -> Temperature { | ||
| 177 | // SMP must be ≥ 56 ADC clock cycles when using HSI14. | ||
| 178 | // | ||
| 179 | // 6.3.19 Temperature sensor characteristics | ||
| 180 | // tstart ≤ 10μs | ||
| 181 | // ts_temp ≥ 4μs | ||
| 182 | unsafe { | ||
| 183 | T::regs().ccr().modify(|reg| reg.set_tsen(true)); | ||
| 184 | } | ||
| 185 | delay.delay_us(10); | ||
| 186 | Temperature | ||
| 187 | } | ||
| 188 | |||
| 189 | fn calibrate(&self) { | ||
| 190 | unsafe { | ||
| 191 | // A.7.1 ADC calibration code example | ||
| 192 | if T::regs().cr().read().aden() { | ||
| 193 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 194 | } | ||
| 195 | while T::regs().cr().read().aden() { | ||
| 196 | // spin | ||
| 197 | } | ||
| 198 | T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); | ||
| 199 | T::regs().cr().modify(|reg| reg.set_adcal(true)); | ||
| 200 | while T::regs().cr().read().adcal() { | ||
| 201 | // spin | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 207 | self.sample_time = sample_time; | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn set_vref_mv(&mut self, vref_mv: u32) { | ||
| 211 | self.vref_mv = vref_mv; | ||
| 212 | } | ||
| 213 | |||
| 214 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 215 | self.resolution = resolution; | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn to_millivolts(&self, sample: u16) -> u16 { | ||
| 219 | ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 | ||
| 220 | } | ||
| 221 | |||
| 222 | fn convert(&mut self) -> u16 { | ||
| 223 | unsafe { | ||
| 224 | T::regs().isr().modify(|reg| { | ||
| 225 | reg.set_eoc(true); | ||
| 226 | reg.set_eosmp(true); | ||
| 227 | }); | ||
| 228 | |||
| 229 | // A.7.5 Single conversion sequence code example - Software trigger | ||
| 230 | T::regs().cr().modify(|reg| reg.set_adstart(true)); | ||
| 231 | while !T::regs().isr().read().eoc() { | ||
| 232 | // spin | ||
| 233 | } | ||
| 234 | |||
| 235 | T::regs().dr().read().0 as u16 | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 { | ||
| 240 | unsafe { | ||
| 241 | // A.7.2 ADC enable sequence code example | ||
| 242 | if T::regs().isr().read().adrdy() { | ||
| 243 | T::regs().isr().modify(|reg| reg.set_adrdy(true)); | ||
| 244 | } | ||
| 245 | T::regs().cr().modify(|reg| reg.set_aden(true)); | ||
| 246 | while !T::regs().isr().read().adrdy() { | ||
| 247 | // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration | ||
| 248 | // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the | ||
| 249 | // ADEN bit until the ADRDY flag goes high. | ||
| 250 | T::regs().cr().modify(|reg| reg.set_aden(true)); | ||
| 251 | } | ||
| 252 | |||
| 253 | T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); | ||
| 254 | Self::set_channel_sample_time(pin.channel(), self.sample_time); | ||
| 255 | T::regs() | ||
| 256 | .chselr() | ||
| 257 | .write(|reg| reg.set_chselx(pin.channel() as usize, true)); | ||
| 258 | |||
| 259 | let value = self.convert(); | ||
| 260 | |||
| 261 | // A.7.3 ADC disable code example | ||
| 262 | T::regs().cr().modify(|reg| reg.set_adstp(true)); | ||
| 263 | while T::regs().cr().read().adstp() { | ||
| 264 | // spin | ||
| 265 | } | ||
| 266 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 267 | while T::regs().cr().read().aden() { | ||
| 268 | // spin | ||
| 269 | } | ||
| 270 | |||
| 271 | value | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 276 | T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); | ||
| 277 | } | ||
| 278 | } | ||
