aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/adc/g4.rs304
-rw-r--r--embassy-stm32/src/adc/mod.rs29
-rw-r--r--examples/stm32g4/src/bin/adc.rs2
3 files changed, 332 insertions, 3 deletions
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
new file mode 100644
index 000000000..f6741f019
--- /dev/null
+++ b/embassy-stm32/src/adc/g4.rs
@@ -0,0 +1,304 @@
1#[allow(unused)]
2use pac::adc::vals::{Adcaldif, Difsel, Exten};
3use pac::adccommon::vals::Presc;
4
5use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
6use crate::time::Hertz;
7use crate::{pac, Peripheral};
8
9/// Default VREF voltage used for sample conversion to millivolts.
10pub const VREF_DEFAULT_MV: u32 = 3300;
11/// VREF voltage used for factory calibration of VREFINTCAL register.
12pub const VREF_CALIB_MV: u32 = 3300;
13
14/// Max single ADC operation clock frequency
15#[cfg(stm32g4)]
16const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
17#[cfg(stm32h7)]
18const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
19
20#[cfg(stm32g4)]
21const VREF_CHANNEL: u8 = 18;
22#[cfg(stm32g4)]
23const TEMP_CHANNEL: u8 = 16;
24
25#[cfg(stm32h7)]
26const VREF_CHANNEL: u8 = 19;
27#[cfg(stm32h7)]
28const TEMP_CHANNEL: u8 = 18;
29
30// TODO this should be 14 for H7a/b/35
31const VBAT_CHANNEL: u8 = 17;
32
33// 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
34/// Internal voltage reference channel.
35pub struct VrefInt;
36impl<T: Instance> InternalChannel<T> for VrefInt {}
37impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
38 fn channel(&self) -> u8 {
39 VREF_CHANNEL
40 }
41}
42
43/// Internal temperature channel.
44pub struct Temperature;
45impl<T: Instance> InternalChannel<T> for Temperature {}
46impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
47 fn channel(&self) -> u8 {
48 TEMP_CHANNEL
49 }
50}
51
52/// Internal battery voltage channel.
53pub struct Vbat;
54impl<T: Instance> InternalChannel<T> for Vbat {}
55impl<T: Instance> super::SealedInternalChannel<T> for Vbat {
56 fn channel(&self) -> u8 {
57 VBAT_CHANNEL
58 }
59}
60
61// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
62// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
63#[allow(unused)]
64enum Prescaler {
65 NotDivided,
66 DividedBy2,
67 DividedBy4,
68 DividedBy6,
69 DividedBy8,
70 DividedBy10,
71 DividedBy12,
72 DividedBy16,
73 DividedBy32,
74 DividedBy64,
75 DividedBy128,
76 DividedBy256,
77}
78
79impl Prescaler {
80 fn from_ker_ck(frequency: Hertz) -> Self {
81 let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0;
82 match raw_prescaler {
83 0 => Self::NotDivided,
84 1 => Self::DividedBy2,
85 2..=3 => Self::DividedBy4,
86 4..=5 => Self::DividedBy6,
87 6..=7 => Self::DividedBy8,
88 8..=9 => Self::DividedBy10,
89 10..=11 => Self::DividedBy12,
90 _ => unimplemented!(),
91 }
92 }
93
94 fn divisor(&self) -> u32 {
95 match self {
96 Prescaler::NotDivided => 1,
97 Prescaler::DividedBy2 => 2,
98 Prescaler::DividedBy4 => 4,
99 Prescaler::DividedBy6 => 6,
100 Prescaler::DividedBy8 => 8,
101 Prescaler::DividedBy10 => 10,
102 Prescaler::DividedBy12 => 12,
103 Prescaler::DividedBy16 => 16,
104 Prescaler::DividedBy32 => 32,
105 Prescaler::DividedBy64 => 64,
106 Prescaler::DividedBy128 => 128,
107 Prescaler::DividedBy256 => 256,
108 }
109 }
110
111 fn presc(&self) -> Presc {
112 match self {
113 Prescaler::NotDivided => Presc::DIV1,
114 Prescaler::DividedBy2 => Presc::DIV2,
115 Prescaler::DividedBy4 => Presc::DIV4,
116 Prescaler::DividedBy6 => Presc::DIV6,
117 Prescaler::DividedBy8 => Presc::DIV8,
118 Prescaler::DividedBy10 => Presc::DIV10,
119 Prescaler::DividedBy12 => Presc::DIV12,
120 Prescaler::DividedBy16 => Presc::DIV16,
121 Prescaler::DividedBy32 => Presc::DIV32,
122 Prescaler::DividedBy64 => Presc::DIV64,
123 Prescaler::DividedBy128 => Presc::DIV128,
124 Prescaler::DividedBy256 => Presc::DIV256,
125 }
126 }
127}
128
129impl<'d, T: Instance> Adc<'d, T> {
130 /// Create a new ADC driver.
131 pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
132 embassy_hal_internal::into_ref!(adc);
133 T::enable_and_reset();
134
135 let prescaler = Prescaler::from_ker_ck(T::frequency());
136
137 T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
138
139 let frequency = Hertz(T::frequency().0 / prescaler.divisor());
140 info!("ADC frequency set to {} Hz", frequency.0);
141
142 if frequency > MAX_ADC_CLK_FREQ {
143 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 );
144 }
145
146 let mut s = Self {
147 adc,
148 sample_time: SampleTime::from_bits(0),
149 };
150 s.power_up();
151 s.configure_differential_inputs();
152
153 s.calibrate();
154 blocking_delay_us(1);
155
156 s.enable();
157 s.configure();
158
159 s
160 }
161
162 fn power_up(&mut self) {
163 T::regs().cr().modify(|reg| {
164 reg.set_deeppwd(false);
165 reg.set_advregen(true);
166 });
167
168 blocking_delay_us(10);
169 }
170
171 fn configure_differential_inputs(&mut self) {
172 T::regs().difsel().modify(|w| {
173 for n in 0..20 {
174 w.set_difsel(n, Difsel::SINGLEENDED);
175 }
176 });
177 }
178
179 fn calibrate(&mut self) {
180 T::regs().cr().modify(|w| {
181 w.set_adcaldif(Adcaldif::SINGLEENDED);
182 });
183
184 T::regs().cr().modify(|w| w.set_adcal(true));
185
186 while T::regs().cr().read().adcal() {}
187 }
188
189 fn enable(&mut self) {
190 T::regs().isr().write(|w| w.set_adrdy(true));
191 T::regs().cr().modify(|w| w.set_aden(true));
192 while !T::regs().isr().read().adrdy() {}
193 T::regs().isr().write(|w| w.set_adrdy(true));
194 }
195
196 fn configure(&mut self) {
197 // single conversion mode, software trigger
198 T::regs().cfgr().modify(|w| {
199 w.set_cont(false);
200 w.set_exten(Exten::DISABLED);
201 });
202 }
203
204 /// Enable reading the voltage reference internal channel.
205 pub fn enable_vrefint(&self) -> VrefInt {
206 T::common_regs().ccr().modify(|reg| {
207 reg.set_vrefen(true);
208 });
209
210 VrefInt {}
211 }
212
213 /// Enable reading the temperature internal channel.
214 pub fn enable_temperature(&self) -> Temperature {
215 T::common_regs().ccr().modify(|reg| {
216 reg.set_vsenseen(true);
217 });
218
219 Temperature {}
220 }
221
222 /// Enable reading the vbat internal channel.
223 pub fn enable_vbat(&self) -> Vbat {
224 T::common_regs().ccr().modify(|reg| {
225 reg.set_vbaten(true);
226 });
227
228 Vbat {}
229 }
230
231 /// Set the ADC sample time.
232 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
233 self.sample_time = sample_time;
234 }
235
236 /// Set the ADC resolution.
237 pub fn set_resolution(&mut self, resolution: Resolution) {
238 T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
239 }
240
241 /// Perform a single conversion.
242 fn convert(&mut self) -> u16 {
243 T::regs().isr().modify(|reg| {
244 reg.set_eos(true);
245 reg.set_eoc(true);
246 });
247
248 // Start conversion
249 T::regs().cr().modify(|reg| {
250 reg.set_adstart(true);
251 });
252
253 while !T::regs().isr().read().eos() {
254 // spin
255 }
256
257 T::regs().dr().read().0 as u16
258 }
259
260 /// Read an ADC pin.
261 pub fn read<P>(&mut self, pin: &mut P) -> u16
262 where
263 P: AdcPin<T>,
264 P: crate::gpio::Pin,
265 {
266 pin.set_as_analog();
267
268 self.read_channel(pin.channel())
269 }
270
271 /// Read an ADC internal channel.
272 pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
273 self.read_channel(channel.channel())
274 }
275
276 fn read_channel(&mut self, channel: u8) -> u16 {
277 // Configure channel
278 Self::set_channel_sample_time(channel, self.sample_time);
279
280 #[cfg(stm32h7)]
281 {
282 T::regs().cfgr2().modify(|w| w.set_lshift(0));
283 T::regs()
284 .pcsel()
285 .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
286 }
287
288 T::regs().sqr1().write(|reg| {
289 reg.set_sq(0, channel);
290 reg.set_l(0);
291 });
292
293 self.convert()
294 }
295
296 fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
297 let sample_time = sample_time.into();
298 if ch <= 9 {
299 T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time));
300 } else {
301 T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
302 }
303 }
304}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 24dd7cc3c..12c5751bd 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -12,6 +12,7 @@
12#[cfg_attr(adc_v2, path = "v2.rs")] 12#[cfg_attr(adc_v2, path = "v2.rs")]
13#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")] 13#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
14#[cfg_attr(adc_v4, path = "v4.rs")] 14#[cfg_attr(adc_v4, path = "v4.rs")]
15#[cfg_attr(adc_g4, path = "g4.rs")]
15mod _version; 16mod _version;
16 17
17#[allow(unused)] 18#[allow(unused)]
@@ -79,13 +80,37 @@ pub(crate) fn blocking_delay_us(us: u32) {
79} 80}
80 81
81/// ADC instance. 82/// ADC instance.
82#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))] 83#[cfg(not(any(
84 adc_f1,
85 adc_v1,
86 adc_l0,
87 adc_v2,
88 adc_v3,
89 adc_v4,
90 adc_g4,
91 adc_f3,
92 adc_f3_v1_1,
93 adc_g0,
94 adc_h5
95)))]
83#[allow(private_bounds)] 96#[allow(private_bounds)]
84pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { 97pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
85 type Interrupt: crate::interrupt::typelevel::Interrupt; 98 type Interrupt: crate::interrupt::typelevel::Interrupt;
86} 99}
87/// ADC instance. 100/// ADC instance.
88#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))] 101#[cfg(any(
102 adc_f1,
103 adc_v1,
104 adc_l0,
105 adc_v2,
106 adc_v3,
107 adc_v4,
108 adc_g4,
109 adc_f3,
110 adc_f3_v1_1,
111 adc_g0,
112 adc_h5
113))]
89#[allow(private_bounds)] 114#[allow(private_bounds)]
90pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral { 115pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {
91 type Interrupt: crate::interrupt::typelevel::Interrupt; 116 type Interrupt: crate::interrupt::typelevel::Interrupt;
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs
index 68b54e406..3de38cbd6 100644
--- a/examples/stm32g4/src/bin/adc.rs
+++ b/examples/stm32g4/src/bin/adc.rs
@@ -29,7 +29,7 @@ async fn main(_spawner: Spawner) {
29 info!("Hello World!"); 29 info!("Hello World!");
30 30
31 let mut adc = Adc::new(p.ADC2); 31 let mut adc = Adc::new(p.ADC2);
32 adc.set_sample_time(SampleTime::CYCLES32_5); 32 adc.set_sample_time(SampleTime::CYCLES24_5);
33 33
34 loop { 34 loop {
35 let measured = adc.read(&mut p.PA7); 35 let measured = adc.read(&mut p.PA7);