diff options
| -rw-r--r-- | embassy-stm32/build.rs | 26 | ||||
| -rw-r--r-- | embassy-stm32/src/opamp.rs | 215 | ||||
| -rw-r--r-- | examples/stm32f334/src/bin/opamp.rs | 2 |
3 files changed, 209 insertions, 34 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 276c1d637..35c3f23b4 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -944,17 +944,25 @@ fn main() { | |||
| 944 | } | 944 | } |
| 945 | 945 | ||
| 946 | if regs.kind == "opamp" { | 946 | if regs.kind == "opamp" { |
| 947 | if !pin.signal.starts_with("VP") { | 947 | println!("{}", pin.signal); |
| 948 | continue; | ||
| 949 | } | ||
| 950 | 948 | ||
| 951 | let peri = format_ident!("{}", p.name); | 949 | if pin.signal.starts_with("VP") { |
| 952 | let pin_name = format_ident!("{}", pin.pin); | 950 | // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) |
| 953 | let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); | 951 | let peri = format_ident!("{}", p.name); |
| 952 | let pin_name = format_ident!("{}", pin.pin); | ||
| 953 | let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); | ||
| 954 | 954 | ||
| 955 | g.extend(quote! { | 955 | g.extend(quote! { |
| 956 | impl_opamp_pin!( #peri, #pin_name, #ch); | 956 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); |
| 957 | }) | 957 | }) |
| 958 | } else if pin.signal == "VOUT" { | ||
| 959 | // Impl OutputPin for the VOUT pin | ||
| 960 | let peri = format_ident!("{}", p.name); | ||
| 961 | let pin_name = format_ident!("{}", pin.pin); | ||
| 962 | g.extend(quote! { | ||
| 963 | impl_opamp_vout_pin!( #peri, #pin_name ); | ||
| 964 | }) | ||
| 965 | } | ||
| 958 | } | 966 | } |
| 959 | 967 | ||
| 960 | // DAC is special | 968 | // DAC is special |
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index cb55cbe19..89db6d14d 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -13,21 +13,50 @@ pub enum OpAmpGain { | |||
| 13 | Mul16, | 13 | Mul16, |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> { | 16 | #[derive(Clone, Copy)] |
| 17 | pub enum OpAmpSpeed { | ||
| 18 | Normal, | ||
| 19 | HighSpeed, | ||
| 20 | } | ||
| 21 | |||
| 22 | #[cfg(opamp_g4)] | ||
| 23 | impl From<OpAmpSpeed> for crate::pac::opamp::vals::OpampCsrOpahsm { | ||
| 24 | fn from(v: OpAmpSpeed) -> Self { | ||
| 25 | match v { | ||
| 26 | OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL, | ||
| 27 | OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED, | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | /// OpAmp external outputs, wired to a GPIO pad. | ||
| 33 | /// | ||
| 34 | /// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere. | ||
| 35 | /// | ||
| 36 | /// This struct can also be used as an ADC input. | ||
| 37 | pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin<T>> { | ||
| 17 | _inner: &'d OpAmp<'d, T>, | 38 | _inner: &'d OpAmp<'d, T>, |
| 18 | _input: &'p mut P, | 39 | _output: &'p mut P, |
| 19 | } | 40 | } |
| 20 | 41 | ||
| 42 | /// OpAmp internal outputs, wired directly to ADC inputs. | ||
| 43 | /// | ||
| 44 | /// This struct can be used as an ADC input. | ||
| 45 | pub struct OpAmpInternalOutput<'d, T: Instance> { | ||
| 46 | _inner: &'d OpAmp<'d, T>, | ||
| 47 | } | ||
| 48 | |||
| 49 | /// OpAmp driver. | ||
| 21 | pub struct OpAmp<'d, T: Instance> { | 50 | pub struct OpAmp<'d, T: Instance> { |
| 22 | _inner: PeripheralRef<'d, T>, | 51 | _inner: PeripheralRef<'d, T>, |
| 23 | } | 52 | } |
| 24 | 53 | ||
| 25 | impl<'d, T: Instance> OpAmp<'d, T> { | 54 | impl<'d, T: Instance> OpAmp<'d, T> { |
| 26 | pub fn new(opamp: impl Peripheral<P = T> + 'd) -> Self { | 55 | /// Create a new driver instance. |
| 27 | Self::new_inner(opamp) | 56 | /// |
| 28 | } | 57 | /// Enables the OpAmp and configures the speed, but |
| 29 | 58 | /// does not set any other configuration. | |
| 30 | fn new_inner(opamp: impl Peripheral<P = T> + 'd) -> Self { | 59 | pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { |
| 31 | into_ref!(opamp); | 60 | into_ref!(opamp); |
| 32 | 61 | ||
| 33 | #[cfg(opamp_f3)] | 62 | #[cfg(opamp_f3)] |
| @@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 38 | #[cfg(opamp_g4)] | 67 | #[cfg(opamp_g4)] |
| 39 | T::regs().opamp_csr().modify(|w| { | 68 | T::regs().opamp_csr().modify(|w| { |
| 40 | w.set_opaen(true); | 69 | w.set_opaen(true); |
| 70 | w.set_opahsm(speed.into()); | ||
| 41 | }); | 71 | }); |
| 42 | 72 | ||
| 43 | Self { _inner: opamp } | 73 | Self { _inner: opamp } |
| 44 | } | 74 | } |
| 45 | 75 | ||
| 46 | pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P> | 76 | /// Configure the OpAmp as a buffer for the provided input pin, |
| 77 | /// outputting to the provided output pin. | ||
| 78 | /// | ||
| 79 | /// The input pin is configured for analogue mode but not consumed, | ||
| 80 | /// so it may subsequently be used for ADC or comparator inputs. | ||
| 81 | /// | ||
| 82 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 83 | /// preventing it being used elsewhere. The `OpAmpOutput` can then be | ||
| 84 | /// directly used as an ADC input. | ||
| 85 | pub fn buffer_ext<'a, 'b, IP, OP>( | ||
| 86 | &'a mut self, | ||
| 87 | in_pin: &IP, | ||
| 88 | out_pin: &'b mut OP, | ||
| 89 | gain: OpAmpGain, | ||
| 90 | ) -> OpAmpOutput<'a, 'b, T, OP> | ||
| 47 | where | 91 | where |
| 48 | P: NonInvertingPin<T>, | 92 | IP: NonInvertingPin<T> + crate::gpio::sealed::Pin, |
| 93 | OP: OutputPin<T> + crate::gpio::sealed::Pin, | ||
| 49 | { | 94 | { |
| 95 | in_pin.set_as_analog(); | ||
| 96 | out_pin.set_as_analog(); | ||
| 97 | |||
| 50 | let (vm_sel, pga_gain) = match gain { | 98 | let (vm_sel, pga_gain) = match gain { |
| 51 | OpAmpGain::Mul1 => (0b11, 0b00), | 99 | OpAmpGain::Mul1 => (0b11, 0b00), |
| 52 | OpAmpGain::Mul2 => (0b10, 0b00), | 100 | OpAmpGain::Mul2 => (0b10, 0b00), |
| @@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 57 | 105 | ||
| 58 | #[cfg(opamp_f3)] | 106 | #[cfg(opamp_f3)] |
| 59 | T::regs().opampcsr().modify(|w| { | 107 | T::regs().opampcsr().modify(|w| { |
| 60 | w.set_vp_sel(pin.channel()); | 108 | w.set_vp_sel(in_pin.channel()); |
| 61 | w.set_vm_sel(vm_sel); | 109 | w.set_vm_sel(vm_sel); |
| 62 | w.set_pga_gain(pga_gain); | 110 | w.set_pga_gain(pga_gain); |
| 111 | w.set_opampen(true); | ||
| 63 | }); | 112 | }); |
| 64 | 113 | ||
| 65 | #[cfg(opamp_g4)] | 114 | #[cfg(opamp_g4)] |
| 66 | T::regs().opamp_csr().modify(|w| { | 115 | T::regs().opamp_csr().modify(|w| { |
| 67 | use crate::pac::opamp::vals::*; | 116 | use crate::pac::opamp::vals::*; |
| 68 | 117 | ||
| 69 | w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); | 118 | w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel())); |
| 70 | w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); | 119 | w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); |
| 71 | w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); | 120 | w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); |
| 121 | w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN); | ||
| 122 | w.set_opaen(true); | ||
| 72 | }); | 123 | }); |
| 73 | 124 | ||
| 74 | OpAmpOutput { | 125 | OpAmpOutput { |
| 75 | _inner: self, | 126 | _inner: self, |
| 76 | _input: pin, | 127 | _output: out_pin, |
| 77 | } | 128 | } |
| 78 | } | 129 | } |
| 130 | |||
| 131 | /// Configure the OpAmp as a buffer for the provided input pin, | ||
| 132 | /// with the output only used internally. | ||
| 133 | /// | ||
| 134 | /// The input pin is configured for analogue mode but not consumed, | ||
| 135 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 136 | /// | ||
| 137 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. | ||
| 138 | #[cfg(opamp_g4)] | ||
| 139 | pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T> | ||
| 140 | where | ||
| 141 | P: NonInvertingPin<T> + crate::gpio::sealed::Pin, | ||
| 142 | { | ||
| 143 | pin.set_as_analog(); | ||
| 144 | |||
| 145 | let (vm_sel, pga_gain) = match gain { | ||
| 146 | OpAmpGain::Mul1 => (0b11, 0b00), | ||
| 147 | OpAmpGain::Mul2 => (0b10, 0b00), | ||
| 148 | OpAmpGain::Mul4 => (0b10, 0b01), | ||
| 149 | OpAmpGain::Mul8 => (0b10, 0b10), | ||
| 150 | OpAmpGain::Mul16 => (0b10, 0b11), | ||
| 151 | }; | ||
| 152 | |||
| 153 | T::regs().opamp_csr().modify(|w| { | ||
| 154 | use crate::pac::opamp::vals::*; | ||
| 155 | w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); | ||
| 156 | w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); | ||
| 157 | w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); | ||
| 158 | w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL); | ||
| 159 | w.set_opaen(true); | ||
| 160 | }); | ||
| 161 | |||
| 162 | OpAmpInternalOutput { _inner: self } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | impl<'d, T: Instance> Drop for OpAmp<'d, T> { | ||
| 167 | fn drop(&mut self) { | ||
| 168 | #[cfg(opamp_f3)] | ||
| 169 | T::regs().opampcsr().modify(|w| { | ||
| 170 | w.set_opampen(false); | ||
| 171 | }); | ||
| 172 | |||
| 173 | #[cfg(opamp_g4)] | ||
| 174 | T::regs().opamp_csr().modify(|w| { | ||
| 175 | w.set_opaen(false); | ||
| 176 | }); | ||
| 177 | } | ||
| 79 | } | 178 | } |
| 80 | 179 | ||
| 81 | pub trait Instance: sealed::Instance + 'static {} | 180 | pub trait Instance: sealed::Instance + 'static {} |
| @@ -92,18 +191,19 @@ pub(crate) mod sealed { | |||
| 92 | pub trait InvertingPin<T: Instance> { | 191 | pub trait InvertingPin<T: Instance> { |
| 93 | fn channel(&self) -> u8; | 192 | fn channel(&self) -> u8; |
| 94 | } | 193 | } |
| 194 | |||
| 195 | pub trait OutputPin<T: Instance> {} | ||
| 95 | } | 196 | } |
| 96 | 197 | ||
| 97 | pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {} | 198 | pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {} |
| 98 | |||
| 99 | pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {} | 199 | pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {} |
| 200 | pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {} | ||
| 100 | 201 | ||
| 101 | #[cfg(opamp_f3)] | 202 | macro_rules! impl_opamp_external_output { |
| 102 | macro_rules! impl_opamp_output { | ||
| 103 | ($inst:ident, $adc:ident, $ch:expr) => { | 203 | ($inst:ident, $adc:ident, $ch:expr) => { |
| 104 | foreach_adc!( | 204 | foreach_adc!( |
| 105 | ($adc, $common_inst:ident, $adc_clock:ident) => { | 205 | ($adc, $common_inst:ident, $adc_clock:ident) => { |
| 106 | impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc> | 206 | impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc> |
| 107 | for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> | 207 | for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> |
| 108 | { | 208 | { |
| 109 | fn channel(&self) -> u8 { | 209 | fn channel(&self) -> u8 { |
| @@ -111,7 +211,7 @@ macro_rules! impl_opamp_output { | |||
| 111 | } | 211 | } |
| 112 | } | 212 | } |
| 113 | 213 | ||
| 114 | impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc> | 214 | impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc> |
| 115 | for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> | 215 | for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> |
| 116 | { | 216 | { |
| 117 | } | 217 | } |
| @@ -120,19 +220,79 @@ macro_rules! impl_opamp_output { | |||
| 120 | }; | 220 | }; |
| 121 | } | 221 | } |
| 122 | 222 | ||
| 123 | #[cfg(opamp_f3)] | ||
| 124 | foreach_peripheral!( | 223 | foreach_peripheral!( |
| 125 | (opamp, OPAMP1) => { | 224 | (opamp, OPAMP1) => { |
| 126 | impl_opamp_output!(OPAMP1, ADC1, 3); | 225 | impl_opamp_external_output!(OPAMP1, ADC1, 3); |
| 226 | }; | ||
| 227 | (opamp, OPAMP2) => { | ||
| 228 | impl_opamp_external_output!(OPAMP2, ADC2, 3); | ||
| 229 | }; | ||
| 230 | (opamp, OPAMP3) => { | ||
| 231 | impl_opamp_external_output!(OPAMP3, ADC3, 1); | ||
| 232 | }; | ||
| 233 | // OPAMP4 only in STM32G4 Cat 3 devices | ||
| 234 | (opamp, OPAMP4) => { | ||
| 235 | impl_opamp_external_output!(OPAMP4, ADC4, 3); | ||
| 236 | }; | ||
| 237 | // OPAMP5 only in STM32G4 Cat 3 devices | ||
| 238 | (opamp, OPAMP5) => { | ||
| 239 | impl_opamp_external_output!(OPAMP5, ADC5, 1); | ||
| 240 | }; | ||
| 241 | // OPAMP6 only in STM32G4 Cat 3/4 devices | ||
| 242 | (opamp, OPAMP6) => { | ||
| 243 | impl_opamp_external_output!(OPAMP6, ADC1, 14); | ||
| 244 | }; | ||
| 245 | ); | ||
| 246 | |||
| 247 | #[cfg(opamp_g4)] | ||
| 248 | macro_rules! impl_opamp_internal_output { | ||
| 249 | ($inst:ident, $adc:ident, $ch:expr) => { | ||
| 250 | foreach_adc!( | ||
| 251 | ($adc, $common_inst:ident, $adc_clock:ident) => { | ||
| 252 | impl<'d> crate::adc::sealed::AdcPin<crate::peripherals::$adc> | ||
| 253 | for OpAmpInternalOutput<'d, crate::peripherals::$inst> | ||
| 254 | { | ||
| 255 | fn channel(&self) -> u8 { | ||
| 256 | $ch | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | impl<'d> crate::adc::AdcPin<crate::peripherals::$adc> | ||
| 261 | for OpAmpInternalOutput<'d, crate::peripherals::$inst> | ||
| 262 | { | ||
| 263 | } | ||
| 264 | }; | ||
| 265 | ); | ||
| 266 | }; | ||
| 267 | } | ||
| 268 | |||
| 269 | #[cfg(opamp_g4)] | ||
| 270 | foreach_peripheral!( | ||
| 271 | (opamp, OPAMP1) => { | ||
| 272 | impl_opamp_internal_output!(OPAMP1, ADC1, 13); | ||
| 127 | }; | 273 | }; |
| 128 | (opamp, OPAMP2) => { | 274 | (opamp, OPAMP2) => { |
| 129 | impl_opamp_output!(OPAMP2, ADC2, 3); | 275 | impl_opamp_internal_output!(OPAMP2, ADC2, 16); |
| 130 | }; | 276 | }; |
| 131 | (opamp, OPAMP3) => { | 277 | (opamp, OPAMP3) => { |
| 132 | impl_opamp_output!(OPAMP3, ADC3, 1); | 278 | impl_opamp_internal_output!(OPAMP3, ADC2, 18); |
| 279 | // Only in Cat 3/4 devices | ||
| 280 | impl_opamp_internal_output!(OPAMP3, ADC3, 13); | ||
| 133 | }; | 281 | }; |
| 282 | // OPAMP4 only in Cat 3 devices | ||
| 134 | (opamp, OPAMP4) => { | 283 | (opamp, OPAMP4) => { |
| 135 | impl_opamp_output!(OPAMP4, ADC4, 3); | 284 | impl_opamp_internal_output!(OPAMP4, ADC5, 5); |
| 285 | }; | ||
| 286 | // OPAMP5 only in Cat 3 devices | ||
| 287 | (opamp, OPAMP5) => { | ||
| 288 | impl_opamp_internal_output!(OPAMP5, ADC5, 3); | ||
| 289 | }; | ||
| 290 | // OPAMP6 only in Cat 3/4 devices | ||
| 291 | (opamp, OPAMP6) => { | ||
| 292 | // Only in Cat 3 devices | ||
| 293 | impl_opamp_internal_output!(OPAMP6, ADC4, 17); | ||
| 294 | // Only in Cat 4 devices | ||
| 295 | impl_opamp_internal_output!(OPAMP6, ADC3, 17); | ||
| 136 | }; | 296 | }; |
| 137 | ); | 297 | ); |
| 138 | 298 | ||
| @@ -145,13 +305,12 @@ foreach_peripheral! { | |||
| 145 | } | 305 | } |
| 146 | 306 | ||
| 147 | impl Instance for crate::peripherals::$inst { | 307 | impl Instance for crate::peripherals::$inst { |
| 148 | |||
| 149 | } | 308 | } |
| 150 | }; | 309 | }; |
| 151 | } | 310 | } |
| 152 | 311 | ||
| 153 | #[allow(unused_macros)] | 312 | #[allow(unused_macros)] |
| 154 | macro_rules! impl_opamp_pin { | 313 | macro_rules! impl_opamp_vp_pin { |
| 155 | ($inst:ident, $pin:ident, $ch:expr) => { | 314 | ($inst:ident, $pin:ident, $ch:expr) => { |
| 156 | impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {} | 315 | impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {} |
| 157 | impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin { | 316 | impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin { |
| @@ -161,3 +320,11 @@ macro_rules! impl_opamp_pin { | |||
| 161 | } | 320 | } |
| 162 | }; | 321 | }; |
| 163 | } | 322 | } |
| 323 | |||
| 324 | #[allow(unused_macros)] | ||
| 325 | macro_rules! impl_opamp_vout_pin { | ||
| 326 | ($inst:ident, $pin:ident) => { | ||
| 327 | impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} | ||
| 328 | impl crate::opamp::sealed::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} | ||
| 329 | }; | ||
| 330 | } | ||
diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 128001bf2..137fc9e66 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs | |||
| @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 39 | 39 | ||
| 40 | let mut vrefint = adc.enable_vref(&mut Delay); | 40 | let mut vrefint = adc.enable_vref(&mut Delay); |
| 41 | let mut temperature = adc.enable_temperature(); | 41 | let mut temperature = adc.enable_temperature(); |
| 42 | let mut buffer = opamp.buffer_for(&mut p.PA7, OpAmpGain::Mul1); | 42 | let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1); |
| 43 | 43 | ||
| 44 | loop { | 44 | loop { |
| 45 | let vref = adc.read(&mut vrefint).await; | 45 | let vref = adc.read(&mut vrefint).await; |
