diff options
| -rw-r--r-- | embassy-stm32/build.rs | 12 | ||||
| -rw-r--r-- | embassy-stm32/src/opamp.rs | 347 | ||||
| -rw-r--r-- | examples/stm32f334/src/bin/opamp.rs | 4 |
3 files changed, 341 insertions, 22 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d965d8732..19851ee66 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1338,6 +1338,18 @@ fn main() { | |||
| 1338 | g.extend(quote! { | 1338 | g.extend(quote! { |
| 1339 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); | 1339 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); |
| 1340 | }) | 1340 | }) |
| 1341 | } else if pin.signal.starts_with("VINM") { | ||
| 1342 | // Impl NonInvertingPin for the VINM* signals ( VINM0, VINM1, etc) | ||
| 1343 | // STM32G4 | ||
| 1344 | let peri = format_ident!("{}", p.name); | ||
| 1345 | let pin_name = format_ident!("{}", pin.pin); | ||
| 1346 | let ch: Result<u8, _> = pin.signal.strip_prefix("VINM").unwrap().parse(); | ||
| 1347 | |||
| 1348 | if let Ok(ch) = ch { | ||
| 1349 | g.extend(quote! { | ||
| 1350 | impl_opamp_vn_pin!( #peri, #pin_name, #ch); | ||
| 1351 | }) | ||
| 1352 | } | ||
| 1341 | } else if pin.signal == "VOUT" { | 1353 | } else if pin.signal == "VOUT" { |
| 1342 | // Impl OutputPin for the VOUT pin | 1354 | // Impl OutputPin for the VOUT pin |
| 1343 | let peri = format_ident!("{}", p.name); | 1355 | let peri = format_ident!("{}", p.name); |
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index a81493c1b..82de4a89b 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -6,15 +6,33 @@ use embassy_hal_internal::PeripheralType; | |||
| 6 | use crate::pac::opamp::vals::*; | 6 | use crate::pac::opamp::vals::*; |
| 7 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | 8 | ||
| 9 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 10 | #[cfg(opamp_g4)] | ||
| 11 | fn blocking_delay_ms(ms: u32) { | ||
| 12 | #[cfg(feature = "time")] | ||
| 13 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 14 | #[cfg(not(feature = "time"))] | ||
| 15 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 16 | } | ||
| 17 | |||
| 9 | /// Gain | 18 | /// Gain |
| 10 | #[allow(missing_docs)] | 19 | #[allow(missing_docs)] |
| 11 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
| 12 | pub enum OpAmpGain { | 21 | pub enum OpAmpGain { |
| 13 | Mul1, | ||
| 14 | Mul2, | 22 | Mul2, |
| 15 | Mul4, | 23 | Mul4, |
| 16 | Mul8, | 24 | Mul8, |
| 17 | Mul16, | 25 | Mul16, |
| 26 | #[cfg(opamp_g4)] | ||
| 27 | Mul32, | ||
| 28 | #[cfg(opamp_g4)] | ||
| 29 | Mul64, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[cfg(opamp_g4)] | ||
| 33 | enum OpAmpDifferentialPair { | ||
| 34 | P, | ||
| 35 | N, | ||
| 18 | } | 36 | } |
| 19 | 37 | ||
| 20 | /// Speed | 38 | /// Speed |
| @@ -82,24 +100,71 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 82 | &mut self, | 100 | &mut self, |
| 83 | in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | 101 | in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| 84 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, | 102 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, |
| 103 | ) -> OpAmpOutput<'_, T> { | ||
| 104 | in_pin.set_as_analog(); | ||
| 105 | out_pin.set_as_analog(); | ||
| 106 | |||
| 107 | #[cfg(opamp_g4)] | ||
| 108 | let vm_sel = VmSel::OUTPUT; | ||
| 109 | #[cfg(not(opamp_g4))] | ||
| 110 | let vm_sel = VmSel::from_bits(0b11); | ||
| 111 | |||
| 112 | T::regs().csr().modify(|w| { | ||
| 113 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | ||
| 114 | w.set_vm_sel(vm_sel); | ||
| 115 | #[cfg(opamp_g4)] | ||
| 116 | w.set_opaintoen(Opaintoen::OUTPUT_PIN); | ||
| 117 | w.set_opampen(true); | ||
| 118 | }); | ||
| 119 | |||
| 120 | OpAmpOutput { _inner: self } | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Configure the OpAmp as a PGA for the provided input pin, | ||
| 124 | /// outputting to the provided output pin, and enable the opamp. | ||
| 125 | /// | ||
| 126 | /// The input pin is configured for analogue mode but not consumed, | ||
| 127 | /// so it may subsequently be used for ADC or comparator inputs. | ||
| 128 | /// | ||
| 129 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 130 | /// preventing it being used elsewhere. The `OpAmpOutput` can then be | ||
| 131 | /// directly used as an ADC input. The opamp will be disabled when the | ||
| 132 | /// [`OpAmpOutput`] is dropped. | ||
| 133 | pub fn pga_ext( | ||
| 134 | &mut self, | ||
| 135 | in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 136 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 85 | gain: OpAmpGain, | 137 | gain: OpAmpGain, |
| 86 | ) -> OpAmpOutput<'_, T> { | 138 | ) -> OpAmpOutput<'_, T> { |
| 87 | in_pin.set_as_analog(); | 139 | in_pin.set_as_analog(); |
| 88 | out_pin.set_as_analog(); | 140 | out_pin.set_as_analog(); |
| 89 | 141 | ||
| 90 | // PGA_GAIN value may have different meaning in different MCU serials, use with caution. | 142 | #[cfg(opamp_g4)] |
| 91 | let (vm_sel, pga_gain) = match gain { | 143 | let vm_sel = VmSel::PGA; |
| 92 | OpAmpGain::Mul1 => (0b11, 0b00), | 144 | #[cfg(not(opamp_g4))] |
| 93 | OpAmpGain::Mul2 => (0b10, 0b00), | 145 | let vm_sel = VmSel::from_bits(0b10); |
| 94 | OpAmpGain::Mul4 => (0b10, 0b01), | 146 | |
| 95 | OpAmpGain::Mul8 => (0b10, 0b10), | 147 | #[cfg(opamp_g4)] |
| 96 | OpAmpGain::Mul16 => (0b10, 0b11), | 148 | let pga_gain = match gain { |
| 149 | OpAmpGain::Mul2 => PgaGain::GAIN2, | ||
| 150 | OpAmpGain::Mul4 => PgaGain::GAIN4, | ||
| 151 | OpAmpGain::Mul8 => PgaGain::GAIN8, | ||
| 152 | OpAmpGain::Mul16 => PgaGain::GAIN16, | ||
| 153 | OpAmpGain::Mul32 => PgaGain::GAIN32, | ||
| 154 | OpAmpGain::Mul64 => PgaGain::GAIN64, | ||
| 97 | }; | 155 | }; |
| 156 | #[cfg(not(opamp_g4))] | ||
| 157 | let pga_gain = PgaGain::from_bits(match gain { | ||
| 158 | OpAmpGain::Mul2 => 0b00, | ||
| 159 | OpAmpGain::Mul4 => 0b01, | ||
| 160 | OpAmpGain::Mul8 => 0b10, | ||
| 161 | OpAmpGain::Mul16 => 0b11, | ||
| 162 | }); | ||
| 98 | 163 | ||
| 99 | T::regs().csr().modify(|w| { | 164 | T::regs().csr().modify(|w| { |
| 100 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | 165 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); |
| 101 | w.set_vm_sel(VmSel::from_bits(vm_sel)); | 166 | w.set_vm_sel(vm_sel); |
| 102 | w.set_pga_gain(PgaGain::from_bits(pga_gain)); | 167 | w.set_pga_gain(pga_gain); |
| 103 | #[cfg(opamp_g4)] | 168 | #[cfg(opamp_g4)] |
| 104 | w.set_opaintoen(Opaintoen::OUTPUT_PIN); | 169 | w.set_opaintoen(Opaintoen::OUTPUT_PIN); |
| 105 | w.set_opampen(true); | 170 | w.set_opampen(true); |
| @@ -107,6 +172,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 107 | 172 | ||
| 108 | OpAmpOutput { _inner: self } | 173 | OpAmpOutput { _inner: self } |
| 109 | } | 174 | } |
| 175 | |||
| 110 | /// Configure the OpAmp as a buffer for the DAC it is connected to, | 176 | /// Configure the OpAmp as a buffer for the DAC it is connected to, |
| 111 | /// outputting to the provided output pin, and enable the opamp. | 177 | /// outputting to the provided output pin, and enable the opamp. |
| 112 | /// | 178 | /// |
| @@ -142,30 +208,259 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 142 | pub fn buffer_int( | 208 | pub fn buffer_int( |
| 143 | &mut self, | 209 | &mut self, |
| 144 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | 210 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| 211 | ) -> OpAmpInternalOutput<'_, T> { | ||
| 212 | pin.set_as_analog(); | ||
| 213 | |||
| 214 | T::regs().csr().modify(|w| { | ||
| 215 | w.set_vp_sel(VpSel::from_bits(pin.channel())); | ||
| 216 | w.set_vm_sel(VmSel::OUTPUT); | ||
| 217 | #[cfg(opamp_g4)] | ||
| 218 | w.set_opaintoen(Opaintoen::ADCCHANNEL); | ||
| 219 | w.set_opampen(true); | ||
| 220 | }); | ||
| 221 | |||
| 222 | OpAmpInternalOutput { _inner: self } | ||
| 223 | } | ||
| 224 | |||
| 225 | /// Configure the OpAmp as a PGA for the provided input pin, | ||
| 226 | /// with the output only used internally, and enable the opamp. | ||
| 227 | /// | ||
| 228 | /// The input pin is configured for analogue mode but not consumed, | ||
| 229 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 230 | /// | ||
| 231 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. | ||
| 232 | /// The opamp output will be disabled when it is dropped. | ||
| 233 | #[cfg(opamp_g4)] | ||
| 234 | pub fn pga_int( | ||
| 235 | &mut self, | ||
| 236 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 145 | gain: OpAmpGain, | 237 | gain: OpAmpGain, |
| 146 | ) -> OpAmpInternalOutput<'_, T> { | 238 | ) -> OpAmpInternalOutput<'_, T> { |
| 147 | pin.set_as_analog(); | 239 | pin.set_as_analog(); |
| 148 | 240 | ||
| 149 | // PGA_GAIN value may have different meaning in different MCU serials, use with caution. | 241 | let pga_gain = match gain { |
| 150 | let (vm_sel, pga_gain) = match gain { | 242 | OpAmpGain::Mul2 => PgaGain::GAIN2, |
| 151 | OpAmpGain::Mul1 => (0b11, 0b00), | 243 | OpAmpGain::Mul4 => PgaGain::GAIN4, |
| 152 | OpAmpGain::Mul2 => (0b10, 0b00), | 244 | OpAmpGain::Mul8 => PgaGain::GAIN8, |
| 153 | OpAmpGain::Mul4 => (0b10, 0b01), | 245 | OpAmpGain::Mul16 => PgaGain::GAIN16, |
| 154 | OpAmpGain::Mul8 => (0b10, 0b10), | 246 | OpAmpGain::Mul32 => PgaGain::GAIN32, |
| 155 | OpAmpGain::Mul16 => (0b10, 0b11), | 247 | OpAmpGain::Mul64 => PgaGain::GAIN64, |
| 156 | }; | 248 | }; |
| 157 | 249 | ||
| 158 | T::regs().csr().modify(|w| { | 250 | T::regs().csr().modify(|w| { |
| 159 | use crate::pac::opamp::vals::*; | ||
| 160 | w.set_vp_sel(VpSel::from_bits(pin.channel())); | 251 | w.set_vp_sel(VpSel::from_bits(pin.channel())); |
| 161 | w.set_vm_sel(VmSel::from_bits(vm_sel)); | 252 | w.set_vm_sel(VmSel::OUTPUT); |
| 162 | w.set_pga_gain(PgaGain::from_bits(pga_gain)); | 253 | w.set_pga_gain(pga_gain); |
| 163 | w.set_opaintoen(Opaintoen::ADCCHANNEL); | 254 | w.set_opaintoen(Opaintoen::ADCCHANNEL); |
| 164 | w.set_opampen(true); | 255 | w.set_opampen(true); |
| 165 | }); | 256 | }); |
| 166 | 257 | ||
| 167 | OpAmpInternalOutput { _inner: self } | 258 | OpAmpInternalOutput { _inner: self } |
| 168 | } | 259 | } |
| 260 | |||
| 261 | /// Configure the OpAmp as a standalone DAC with the inverting input | ||
| 262 | /// connected to the provided pin, and the output is connected | ||
| 263 | /// internally to an ADC channel. | ||
| 264 | /// | ||
| 265 | /// The input pin is configured for analogue mode but not consumed, | ||
| 266 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 267 | /// | ||
| 268 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC | ||
| 269 | /// input. The opamp output will be disabled when it is dropped. | ||
| 270 | #[cfg(opamp_g4)] | ||
| 271 | pub fn standalone_dac_int( | ||
| 272 | &mut self, | ||
| 273 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 274 | ) -> OpAmpInternalOutput<'_, T> { | ||
| 275 | m_pin.set_as_analog(); | ||
| 276 | |||
| 277 | T::regs().csr().modify(|w| { | ||
| 278 | use crate::pac::opamp::vals::*; | ||
| 279 | w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx | ||
| 280 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 281 | w.set_opaintoen(Opaintoen::ADCCHANNEL); | ||
| 282 | w.set_opampen(true); | ||
| 283 | }); | ||
| 284 | |||
| 285 | OpAmpInternalOutput { _inner: self } | ||
| 286 | } | ||
| 287 | |||
| 288 | /// Configure the OpAmp as a standalone DAC with the inverting input | ||
| 289 | /// connected to the provided pin, and the output connected to the | ||
| 290 | /// provided pin. | ||
| 291 | /// | ||
| 292 | /// The input pin is configured for analogue mode but not consumed, | ||
| 293 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 294 | /// | ||
| 295 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 296 | /// preventing it being used elsewhere. The opamp will be disabled when | ||
| 297 | /// the [`OpAmpOutput`] is dropped. | ||
| 298 | #[cfg(opamp_g4)] | ||
| 299 | pub fn standalone_dac_ext( | ||
| 300 | &mut self, | ||
| 301 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 302 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 303 | ) -> OpAmpOutput<'_, T> { | ||
| 304 | m_pin.set_as_analog(); | ||
| 305 | out_pin.set_as_analog(); | ||
| 306 | |||
| 307 | T::regs().csr().modify(|w| { | ||
| 308 | use crate::pac::opamp::vals::*; | ||
| 309 | w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx | ||
| 310 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 311 | w.set_opaintoen(Opaintoen::OUTPUT_PIN); | ||
| 312 | w.set_opampen(true); | ||
| 313 | }); | ||
| 314 | |||
| 315 | OpAmpOutput { _inner: self } | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Configure the OpAmp in standalone mode with the non-inverting input | ||
| 319 | /// connected to the provided `p_pin`, the inverting input connected to | ||
| 320 | /// the `m_pin`, and output to the provided `out_pin`. | ||
| 321 | /// | ||
| 322 | /// The input pins are configured for analogue mode but not consumed, | ||
| 323 | /// allowing their subsequent use for ADC or comparator inputs. | ||
| 324 | /// | ||
| 325 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 326 | /// preventing it being used elsewhere. The opamp will be disabled when | ||
| 327 | /// the [`OpAmpOutput`] is dropped. | ||
| 328 | #[cfg(opamp_g4)] | ||
| 329 | pub fn standalone_ext( | ||
| 330 | &mut self, | ||
| 331 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 332 | m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 333 | out_pin: Peri<'d, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 334 | ) -> OpAmpOutput<'_, T> { | ||
| 335 | p_pin.set_as_analog(); | ||
| 336 | m_pin.set_as_analog(); | ||
| 337 | out_pin.set_as_analog(); | ||
| 338 | |||
| 339 | T::regs().csr().modify(|w| { | ||
| 340 | use crate::pac::opamp::vals::*; | ||
| 341 | w.set_vp_sel(VpSel::from_bits(p_pin.channel())); | ||
| 342 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 343 | w.set_opaintoen(Opaintoen::OUTPUT_PIN); | ||
| 344 | w.set_opampen(true); | ||
| 345 | }); | ||
| 346 | |||
| 347 | OpAmpOutput { _inner: self } | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Configure the OpAmp in standalone mode with the non-inverting input | ||
| 351 | /// connected to the provided `p_pin`, the inverting input connected to | ||
| 352 | /// the `m_pin`, and output is connected to the DAC. | ||
| 353 | /// | ||
| 354 | /// The input pins are configured for analogue mode but not consumed, | ||
| 355 | /// allowing their subsequent use for ADC or comparator inputs. | ||
| 356 | /// | ||
| 357 | /// The returned `OpAmpOutput` struct may be used as an ADC | ||
| 358 | /// input. The opamp output will be disabled when it is dropped. | ||
| 359 | #[cfg(opamp_g4)] | ||
| 360 | pub fn standalone_int( | ||
| 361 | &mut self, | ||
| 362 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 363 | m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 364 | ) -> OpAmpOutput<'_, T> { | ||
| 365 | p_pin.set_as_analog(); | ||
| 366 | m_pin.set_as_analog(); | ||
| 367 | |||
| 368 | T::regs().csr().modify(|w| { | ||
| 369 | use crate::pac::opamp::vals::*; | ||
| 370 | w.set_vp_sel(VpSel::from_bits(p_pin.channel())); | ||
| 371 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 372 | w.set_opaintoen(Opaintoen::ADCCHANNEL); | ||
| 373 | w.set_opampen(true); | ||
| 374 | }); | ||
| 375 | |||
| 376 | OpAmpOutput { _inner: self } | ||
| 377 | } | ||
| 378 | |||
| 379 | /// Calibrates the operational amplifier. | ||
| 380 | /// | ||
| 381 | /// This function enables the opamp and sets the user trim mode for calibration. | ||
| 382 | /// Depending on the speed mode of the opamp, it calibrates the differential pair inputs. | ||
| 383 | /// For normal speed, both the P and N differential pairs are calibrated, | ||
| 384 | /// while for high-speed mode, only the P differential pair is calibrated. | ||
| 385 | /// | ||
| 386 | /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). | ||
| 387 | #[cfg(opamp_g4)] | ||
| 388 | pub fn calibrate(&mut self) { | ||
| 389 | T::regs().csr().modify(|w| { | ||
| 390 | w.set_opampen(true); | ||
| 391 | w.set_calon(true); | ||
| 392 | w.set_usertrim(Usertrim::USER); | ||
| 393 | }); | ||
| 394 | |||
| 395 | match T::regs().csr().read().opahsm() { | ||
| 396 | Opahsm::NORMAL => { | ||
| 397 | self.calibrate_differential_pair(OpAmpDifferentialPair::P); | ||
| 398 | self.calibrate_differential_pair(OpAmpDifferentialPair::N); | ||
| 399 | } | ||
| 400 | Opahsm::HIGH_SPEED => { | ||
| 401 | self.calibrate_differential_pair(OpAmpDifferentialPair::P); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | T::regs().csr().modify(|w| { | ||
| 406 | w.set_calon(false); | ||
| 407 | w.set_opampen(false); | ||
| 408 | }); | ||
| 409 | } | ||
| 410 | |||
| 411 | /// Calibrate differential pair. | ||
| 412 | /// | ||
| 413 | /// The calibration is done by trying different offset values and | ||
| 414 | /// measuring the outcal bit. | ||
| 415 | /// | ||
| 416 | /// The calibration range is from 0 to 31. | ||
| 417 | /// | ||
| 418 | /// The result is stored in the OPAMP_CSR register. | ||
| 419 | #[cfg(opamp_g4)] | ||
| 420 | fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { | ||
| 421 | let mut low = 0; | ||
| 422 | let mut high = 31; | ||
| 423 | |||
| 424 | let calsel = match pair { | ||
| 425 | OpAmpDifferentialPair::P => Calsel::PERCENT10, | ||
| 426 | OpAmpDifferentialPair::N => Calsel::PERCENT90, | ||
| 427 | }; | ||
| 428 | |||
| 429 | T::regs().csr().modify(|w| { | ||
| 430 | w.set_calsel(calsel); | ||
| 431 | }); | ||
| 432 | |||
| 433 | while low <= high { | ||
| 434 | let mid = (low + high) / 2; | ||
| 435 | |||
| 436 | T::regs().csr().modify(|w| match pair { | ||
| 437 | OpAmpDifferentialPair::P => { | ||
| 438 | defmt::info!("p calibration. offset: {}", mid); | ||
| 439 | w.set_trimoffsetp(mid); | ||
| 440 | } | ||
| 441 | OpAmpDifferentialPair::N => { | ||
| 442 | defmt::info!("n calibration. offset: {}", mid); | ||
| 443 | w.set_trimoffsetn(mid); | ||
| 444 | } | ||
| 445 | }); | ||
| 446 | |||
| 447 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize | ||
| 448 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 | ||
| 449 | blocking_delay_ms(2); | ||
| 450 | |||
| 451 | if T::regs().csr().read().outcal() == Outcal::LOW { | ||
| 452 | if mid == 0 { | ||
| 453 | break; | ||
| 454 | } | ||
| 455 | high = mid - 1; | ||
| 456 | } else { | ||
| 457 | if mid == 31 { | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | low = mid + 1; | ||
| 461 | } | ||
| 462 | } | ||
| 463 | } | ||
| 169 | } | 464 | } |
| 170 | 465 | ||
| 171 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { | 466 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { |
| @@ -339,6 +634,18 @@ macro_rules! impl_opamp_vp_pin { | |||
| 339 | } | 634 | } |
| 340 | 635 | ||
| 341 | #[allow(unused_macros)] | 636 | #[allow(unused_macros)] |
| 637 | macro_rules! impl_opamp_vn_pin { | ||
| 638 | ($inst:ident, $pin:ident, $ch:expr) => { | ||
| 639 | impl crate::opamp::InvertingPin<peripherals::$inst> for crate::peripherals::$pin {} | ||
| 640 | impl crate::opamp::SealedInvertingPin<peripherals::$inst> for crate::peripherals::$pin { | ||
| 641 | fn channel(&self) -> u8 { | ||
| 642 | $ch | ||
| 643 | } | ||
| 644 | } | ||
| 645 | }; | ||
| 646 | } | ||
| 647 | |||
| 648 | #[allow(unused_macros)] | ||
| 342 | macro_rules! impl_opamp_vout_pin { | 649 | macro_rules! impl_opamp_vout_pin { |
| 343 | ($inst:ident, $pin:ident) => { | 650 | ($inst:ident, $pin:ident) => { |
| 344 | impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} | 651 | impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} |
diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index b30445ead..c344935d7 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 7 | use embassy_stm32::opamp::{OpAmp, OpAmpGain}; | 7 | use embassy_stm32::opamp::OpAmp; |
| 8 | use embassy_stm32::peripherals::ADC2; | 8 | use embassy_stm32::peripherals::ADC2; |
| 9 | use embassy_stm32::time::mhz; | 9 | use embassy_stm32::time::mhz; |
| 10 | use embassy_stm32::{adc, bind_interrupts, Config}; | 10 | use embassy_stm32::{adc, bind_interrupts, Config}; |
| @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 48 | 48 | ||
| 49 | let mut vrefint = adc.enable_vref(); | 49 | let mut vrefint = adc.enable_vref(); |
| 50 | let mut temperature = adc.enable_temperature(); | 50 | let mut temperature = adc.enable_temperature(); |
| 51 | let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow(), OpAmpGain::Mul1); | 51 | let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow()); |
| 52 | 52 | ||
| 53 | loop { | 53 | loop { |
| 54 | let vref = adc.read(&mut vrefint).await; | 54 | let vref = adc.read(&mut vrefint).await; |
