aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/build.rs26
-rw-r--r--embassy-stm32/src/opamp.rs215
-rw-r--r--examples/stm32f334/src/bin/opamp.rs2
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
16pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> { 16#[derive(Clone, Copy)]
17pub enum OpAmpSpeed {
18 Normal,
19 HighSpeed,
20}
21
22#[cfg(opamp_g4)]
23impl 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.
37pub 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.
45pub struct OpAmpInternalOutput<'d, T: Instance> {
46 _inner: &'d OpAmp<'d, T>,
47}
48
49/// OpAmp driver.
21pub struct OpAmp<'d, T: Instance> { 50pub struct OpAmp<'d, T: Instance> {
22 _inner: PeripheralRef<'d, T>, 51 _inner: PeripheralRef<'d, T>,
23} 52}
24 53
25impl<'d, T: Instance> OpAmp<'d, T> { 54impl<'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
166impl<'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
81pub trait Instance: sealed::Instance + 'static {} 180pub 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
97pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {} 198pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
98
99pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {} 199pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
200pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
100 201
101#[cfg(opamp_f3)] 202macro_rules! impl_opamp_external_output {
102macro_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)]
124foreach_peripheral!( 223foreach_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)]
248macro_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)]
270foreach_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)]
154macro_rules! impl_opamp_pin { 313macro_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)]
325macro_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;