|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | + |
| 4 | +#[path = "../examples/utils/mod.rs"] |
| 5 | +mod utils; |
| 6 | + |
| 7 | +use utils::logger::debug; |
| 8 | + |
| 9 | +use core::ops::FnMut; |
| 10 | +use core::result::Result; |
| 11 | +use fugit::{ExtU32, HertzU32, MicrosDurationU32}; |
| 12 | +use hal::delay::DelayExt; |
| 13 | +use hal::stm32; |
| 14 | +use stm32g4xx_hal as hal; |
| 15 | + |
| 16 | +pub const F_SYS: HertzU32 = HertzU32::MHz(16); |
| 17 | +pub const CYCLES_PER_US: u32 = F_SYS.raw() / 1_000_000; |
| 18 | + |
| 19 | +pub fn enable_timer(cp: &mut stm32::CorePeripherals) { |
| 20 | + cp.DCB.enable_trace(); |
| 21 | + cp.DWT.enable_cycle_counter(); |
| 22 | +} |
| 23 | + |
| 24 | +pub fn now() -> MicrosDurationU32 { |
| 25 | + (stm32::DWT::cycle_count() / CYCLES_PER_US).micros() |
| 26 | +} |
| 27 | + |
| 28 | +#[defmt_test::tests] |
| 29 | +mod tests { |
| 30 | + use embedded_hal::{ |
| 31 | + delay::DelayNs, |
| 32 | + digital::{InputPin, OutputPin}, |
| 33 | + pwm::SetDutyCycle, |
| 34 | + }; |
| 35 | + use fixed::types::I1F15; |
| 36 | + use fugit::RateExtU32; |
| 37 | + use stm32g4xx_hal::{ |
| 38 | + adc::{self, AdcClaim, Temperature, Vref}, |
| 39 | + cordic::{ |
| 40 | + op::{dynamic::Mode, Magnitude, SinCos, Sqrt}, |
| 41 | + prec::P60, |
| 42 | + scale::N0, |
| 43 | + types::{Q15, Q31}, |
| 44 | + Ext, |
| 45 | + }, |
| 46 | + dac::{DacExt, DacOut}, |
| 47 | + delay::SYSTDelayExt, |
| 48 | + gpio::{GpioExt, AF6}, |
| 49 | + pwm::PwmExt, |
| 50 | + rcc::RccExt, |
| 51 | + signature::{VrefCal, VDDA_CALIB}, |
| 52 | + stm32::GPIOA, |
| 53 | + }; |
| 54 | + |
| 55 | + #[test] |
| 56 | + fn gpio_push_pull() { |
| 57 | + use super::*; |
| 58 | + |
| 59 | + // TODO: Is it ok to steal these? |
| 60 | + let cp = unsafe { stm32::CorePeripherals::steal() }; |
| 61 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 62 | + let mut rcc = dp.RCC.constrain(); |
| 63 | + let mut delay = cp.SYST.delay(&rcc.clocks); |
| 64 | + defmt::dbg!(rcc.clocks.sys_clk); |
| 65 | + |
| 66 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 67 | + let mut pin = gpioa.pa8.into_push_pull_output(); |
| 68 | + |
| 69 | + pin.set_high().unwrap(); |
| 70 | + delay.delay(1.millis()); // Give the pin plenty of time to go high |
| 71 | + assert!(pin.is_high().unwrap()); |
| 72 | + { |
| 73 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 74 | + assert!(!is_pax_low(gpioa, 8)); |
| 75 | + } |
| 76 | + |
| 77 | + pin.set_low().unwrap(); |
| 78 | + delay.delay(1.millis()); // Give the pin plenty of time to go low |
| 79 | + assert!(pin.is_low().unwrap()); |
| 80 | + { |
| 81 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 82 | + assert!(is_pax_low(gpioa, 8)); |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + #[test] |
| 87 | + fn gpio_open_drain() { |
| 88 | + use super::*; |
| 89 | + |
| 90 | + // TODO: Is it ok to steal these? |
| 91 | + let cp = unsafe { stm32::CorePeripherals::steal() }; |
| 92 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 93 | + let mut rcc = dp.RCC.constrain(); |
| 94 | + let mut delay = cp.SYST.delay(&rcc.clocks); |
| 95 | + |
| 96 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 97 | + let mut pin = gpioa.pa8.into_open_drain_output(); |
| 98 | + |
| 99 | + // Enable pull-up resistor |
| 100 | + { |
| 101 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 102 | + gpioa.pupdr().modify(|_, w| w.pupdr8().pull_up()); |
| 103 | + } |
| 104 | + |
| 105 | + pin.set_high().unwrap(); |
| 106 | + delay.delay(1.millis()); // Give the pin plenty of time to go high |
| 107 | + assert!(pin.is_high().unwrap()); |
| 108 | + { |
| 109 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 110 | + assert!(!is_pax_low(gpioa, 8)); |
| 111 | + } |
| 112 | + |
| 113 | + pin.set_low().unwrap(); |
| 114 | + delay.delay(1.millis()); // Give the pin plenty of time to go low |
| 115 | + assert!(pin.is_low().unwrap()); |
| 116 | + { |
| 117 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 118 | + assert!(is_pax_low(gpioa, 8)); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + #[test] |
| 123 | + fn pwm() { |
| 124 | + use super::*; |
| 125 | + |
| 126 | + // TODO: Is it ok to steal these? |
| 127 | + let mut cp = unsafe { stm32::CorePeripherals::steal() }; |
| 128 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 129 | + enable_timer(&mut cp); |
| 130 | + |
| 131 | + let mut rcc = dp.RCC.constrain(); |
| 132 | + assert_eq!(rcc.clocks.sys_clk, F_SYS); |
| 133 | + |
| 134 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 135 | + let pin: stm32g4xx_hal::gpio::gpioa::PA8<stm32g4xx_hal::gpio::Alternate<AF6>> = |
| 136 | + gpioa.pa8.into_alternate(); |
| 137 | + |
| 138 | + let mut pwm = dp.TIM1.pwm(pin, 1000u32.Hz(), &mut rcc); |
| 139 | + |
| 140 | + pwm.set_duty_cycle_percent(50).unwrap(); |
| 141 | + pwm.enable(); |
| 142 | + |
| 143 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 144 | + |
| 145 | + let min: MicrosDurationU32 = 495u32.micros(); |
| 146 | + let max: MicrosDurationU32 = 505u32.micros(); |
| 147 | + |
| 148 | + debug!("Awaiting first rising edge..."); |
| 149 | + let duration_until_lo = await_lo(&gpioa, max).unwrap(); |
| 150 | + let first_lo_duration = await_hi(&gpioa, max).unwrap(); |
| 151 | + |
| 152 | + let mut hi_duration = 0.micros(); |
| 153 | + let mut lo_duration = 0.micros(); |
| 154 | + |
| 155 | + for _ in 0..10 { |
| 156 | + // Make sure the timer half periods are within 495-505us |
| 157 | + |
| 158 | + hi_duration = await_lo(&gpioa, max).unwrap(); |
| 159 | + assert!( |
| 160 | + hi_duration > min && hi_duration < max, |
| 161 | + "hi: {} < {} < {}", |
| 162 | + min, |
| 163 | + hi_duration, |
| 164 | + max |
| 165 | + ); |
| 166 | + |
| 167 | + lo_duration = await_hi(&gpioa, max).unwrap(); |
| 168 | + assert!( |
| 169 | + lo_duration > min && lo_duration < max, |
| 170 | + "lo: {} < {} < {}", |
| 171 | + min, |
| 172 | + lo_duration, |
| 173 | + max |
| 174 | + ); |
| 175 | + } |
| 176 | + |
| 177 | + // Prints deferred until here to not mess up timing |
| 178 | + debug!("Waited ~{} until low", duration_until_lo); |
| 179 | + debug!("First low half period: {}", first_lo_duration); |
| 180 | + |
| 181 | + debug!("High half period: {}", hi_duration); |
| 182 | + debug!("Low half period: {}", lo_duration); |
| 183 | + |
| 184 | + debug!("Done!"); |
| 185 | + |
| 186 | + pwm.disable(); |
| 187 | + } |
| 188 | + |
| 189 | + #[test] |
| 190 | + fn cordic() { |
| 191 | + fn is_almost_equals(a: f32, b: f32) -> bool { |
| 192 | + (a - b).abs() < 0.001 |
| 193 | + } |
| 194 | + |
| 195 | + use super::*; |
| 196 | + |
| 197 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 198 | + let mut rcc = dp.RCC.constrain(); |
| 199 | + |
| 200 | + let mut cordic = dp |
| 201 | + .CORDIC |
| 202 | + .constrain(&mut rcc) |
| 203 | + .freeze::<Q15, Q31, P60, SinCos>(); // 16 bit arguments, 32 bit results, compute sine and cosine, 60 iterations |
| 204 | + |
| 205 | + // static operation (zero overhead) |
| 206 | + |
| 207 | + cordic.start(I1F15::from_num(-0.25 /* -45 degreees */)); |
| 208 | + |
| 209 | + let (sin, cos) = cordic.result(); |
| 210 | + |
| 211 | + debug!("sin: {}, cos: {}", sin.to_num::<f32>(), cos.to_num::<f32>()); |
| 212 | + assert!(is_almost_equals(sin.to_num::<f32>(), -0.707)); |
| 213 | + assert!(is_almost_equals(cos.to_num::<f32>(), 0.707)); |
| 214 | + |
| 215 | + // dynamic operation |
| 216 | + |
| 217 | + let mut cordic = cordic.into_dynamic(); |
| 218 | + |
| 219 | + let sqrt = cordic.run::<Sqrt<N0>>(I1F15::from_num(0.25)); |
| 220 | + debug!("sqrt: {}", sqrt.to_num::<f32>()); |
| 221 | + assert!(is_almost_equals(sqrt.to_num::<f32>(), 0.5)); |
| 222 | + let magnitude = cordic.run::<Magnitude>((I1F15::from_num(0.25), I1F15::from_num(0.5))); |
| 223 | + debug!("magnitude: {}", magnitude.to_num::<f32>()); |
| 224 | + assert!(is_almost_equals(magnitude.to_num::<f32>(), 0.559)); |
| 225 | + } |
| 226 | + |
| 227 | + #[test] |
| 228 | + fn adc() { |
| 229 | + use super::*; |
| 230 | + |
| 231 | + // TODO: Is it ok to steal these? |
| 232 | + let cp = unsafe { stm32::CorePeripherals::steal() }; |
| 233 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 234 | + let rcc = dp.RCC.constrain(); |
| 235 | + let mut delay = cp.SYST.delay(&rcc.clocks); |
| 236 | + |
| 237 | + let mut adc = dp |
| 238 | + .ADC1 |
| 239 | + .claim(adc::ClockSource::SystemClock, &rcc, &mut delay, true); |
| 240 | + |
| 241 | + adc.enable_temperature(&dp.ADC12_COMMON); |
| 242 | + adc.enable_vref(&dp.ADC12_COMMON); |
| 243 | + let sample_time = adc::config::SampleTime::Cycles_640_5; |
| 244 | + |
| 245 | + let vref = adc.convert(&Vref, sample_time); |
| 246 | + let vref_cal = VrefCal::get().read(); |
| 247 | + let vdda = VDDA_CALIB * vref_cal as u32 / vref as u32; |
| 248 | + debug!("vdda: {}mV", vdda); |
| 249 | + assert!((3200..3400).contains(&vdda)); |
| 250 | + |
| 251 | + let vref = Vref::sample_to_millivolts_ext(vref, vdda, adc::config::Resolution::Twelve); |
| 252 | + debug!("vref: {}mV", vref); |
| 253 | + assert!((1182..1232).contains(&vref)); // From G474 datasheet |
| 254 | + |
| 255 | + let temperature_reading = adc.convert(&Temperature, sample_time); |
| 256 | + let temp = Temperature::temperature_to_degrees_centigrade( |
| 257 | + temperature_reading, |
| 258 | + vdda as f32 / 1000., |
| 259 | + adc::config::Resolution::Twelve, |
| 260 | + ); |
| 261 | + debug!("temp: {}°C", temp); |
| 262 | + assert!((20.0..30.0).contains(&temp), "20.0 < {} < 30.0", temp); |
| 263 | + } |
| 264 | + |
| 265 | + #[test] |
| 266 | + fn dac() { |
| 267 | + use super::*; |
| 268 | + |
| 269 | + // TODO: Is it ok to steal these? |
| 270 | + let cp = unsafe { stm32::CorePeripherals::steal() }; |
| 271 | + let dp = unsafe { stm32::Peripherals::steal() }; |
| 272 | + let mut rcc = dp.RCC.constrain(); |
| 273 | + let mut delay = cp.SYST.delay(&rcc.clocks); |
| 274 | + |
| 275 | + let gpioa = dp.GPIOA.split(&mut rcc); |
| 276 | + let pa4 = gpioa.pa4.into_floating_input(); |
| 277 | + let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc); |
| 278 | + |
| 279 | + let gpioa = unsafe { &*GPIOA::PTR }; |
| 280 | + |
| 281 | + // dac_manual will have its value set manually |
| 282 | + let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(); |
| 283 | + |
| 284 | + dac.set_value(0); |
| 285 | + delay.delay_ms(1); |
| 286 | + assert!(is_pax_low(&gpioa, 4)); |
| 287 | + |
| 288 | + dac.set_value(4095); |
| 289 | + delay.delay_ms(1); |
| 290 | + assert!(!is_pax_low(&gpioa, 4)); |
| 291 | + } |
| 292 | +} |
| 293 | + |
| 294 | +fn is_pax_low(gpioa: &stm32::gpioa::RegisterBlock, x: u8) -> bool { |
| 295 | + gpioa.idr().read().idr(x).is_low() |
| 296 | +} |
| 297 | + |
| 298 | +#[derive(Debug, defmt::Format)] |
| 299 | +struct ErrorTimedOut; |
| 300 | + |
| 301 | +fn await_lo( |
| 302 | + gpioa: &stm32::gpioa::RegisterBlock, |
| 303 | + timeout: MicrosDurationU32, |
| 304 | +) -> Result<MicrosDurationU32, ErrorTimedOut> { |
| 305 | + await_p(|| is_pax_low(gpioa, 8), timeout) |
| 306 | +} |
| 307 | + |
| 308 | +fn await_hi( |
| 309 | + gpioa: &stm32::gpioa::RegisterBlock, |
| 310 | + timeout: MicrosDurationU32, |
| 311 | +) -> Result<MicrosDurationU32, ErrorTimedOut> { |
| 312 | + await_p(|| !is_pax_low(gpioa, 8), timeout) |
| 313 | +} |
| 314 | + |
| 315 | +fn await_p( |
| 316 | + mut p: impl FnMut() -> bool, |
| 317 | + timeout: MicrosDurationU32, |
| 318 | +) -> Result<MicrosDurationU32, ErrorTimedOut> { |
| 319 | + let before = now(); |
| 320 | + |
| 321 | + loop { |
| 322 | + let passed_time = now() - before; |
| 323 | + if p() { |
| 324 | + return Ok(passed_time); |
| 325 | + } |
| 326 | + if passed_time > timeout { |
| 327 | + return Err(ErrorTimedOut); |
| 328 | + } |
| 329 | + } |
| 330 | +} |
0 commit comments