#![no_main] #![no_std] // MCU is STM32F303VCT6 // // Which GPIOs are free to use?? // Rather, which are taken? FROM THE DISCOVERY PDF: // PA0 - AIN_1 with some filtering // PA2 - STLINK_TX // PA3 - STLINK_RX // PA5 - SCL/SPC, T_JTCK // PA6 - SA0/SDO, T_JTDO // PA7 - SDA/SDI/SDO, T_JTDI // PA8 - MCO (with LPF) // PA9 - maybe tied to PA11 // PA10 - maybe tied to PA12 // PA11 - D-, maybe tied to PA9 // PA12 - D+, maybe tied to PA10 // PA13 - TMS/SWDIO // PA14 - TCK/SWCLK // PA15 - JTDI // PB0 - T_NRST // PB2 - GND // PB3 - T_SWO // PB4 - JNTRST // PB5 - SWIM_RST_IN // PB6 - SWIM_RST, SCL // PB7 - SWIM_IN, SDA // PB8 - SWIM // PB9 - SWIM_IN // PB10 - SWIM_IN // PB11 - SWIM // PB12 - T_SWDIO_IN connected to PB14 // PB13 - T_JTCK // PB14 - T_JTMS connected to PB12 // PC4 - USARTI_RX // PC5 - USARTI_TX // PC13 - 10k pulldown // PC14 - 10k pulldown (not fitted) tied to PC14-OSC32_IN // PC15 - tied to PC15-OSC32_OUT // PE0 - INT1 // PE1 - DRDY/INT2 // PE2 - DRDY // PE3 - CS_I2C/SPI // PE4 - INT1 // PE5 - INT2 // PE8 - LD4 blue // PE9 - LD3 red // PE10 - LD5 orange // PE11 - LD7 green // PE12 - LD9 blue // PE13 - LD10 red // PE14 - LD8 orange // PE15 - LD6 green // PF0 - tied to PF0-OSC_IN // PF1 - tied to PF1-OSC_OUT // // It looks like Port D is safe to use, most of port c // // WHICH PINS CAN BE ROUTED TO THE ADC? (from the device data sheet) // PA0 - ADC1_IN1 // PA1 - ADC1_IN2 // PA2 - ADC1_IN3 // PA3 - ADC1_IN4 // PA4 - ADC2_IN1 // PA5 - ADC2_IN2 // PA6 - ADC2_IN3 // PA7 - ADC2_IN4 // PB0 - ADC3_IN12 // PB1 - ADC3_IN1 // PB2 - ADC2_IN12 // PB12 - ADC4_IN3 // PB13 - ADC3_IN5 // PB14 - ADC4_IN4 // PB15 - ADC4_IN5 // PC0 - ADC12_IN6 // PC1 - ADC12_IN7 // PC2 - ADC12_IN8 // PC3 - ADC12_IN9 // PC4 - ADC2_IN5 // PC5 - ADC2_IN11 // PD8 - ADC4_IN12 // PD9 - ADC4_IN12 // PD10 - ADC34_IN7 // PD11 - ADC34_IN8 // PD12 - ADC34_IN8 // PD12 - ADC34_IN9 // PD13 - ADC34_IN10 // PD14 - ADC34_IN11 // PE7 - ADC3_IN13 // PE9 - ADC3_IN2 // PE10 - ADC3_IN14 // PE11 - ADC3_IN15 // PE12 - ADC3_IN16 // PE13 - ADC3_IN3 // PE14 - ADC4_IN1 // PE15 - ADC4_IN2 // PF2 - ADC12_IN10 // PF4 - ADC1_IN5 // // PC0-3 look safe to use as ADC. PF2,4 as well. // // ANALOG INPUT // from 11.3.2: // For the ADC, DAC, OPAMP, and COMP, configure the desired I/O in analog mode // in the GPIOx_MODER register and configure the required function in the ADC, // DAC, OPAMP, and COMP registers. // // Need to generate ADC12_CK or ADC34_CK from RCC. Or derived from AHB bus clock. See CKMODE[1:0] of the ADCx_CCR extern crate panic_itm; // panic handler mod bsp; use cortex_m::{iprintln, itm}; use cortex_m::asm::{bkpt, delay}; use cortex_m_rt::entry; use pdqsort; #[entry] fn main() -> ! { let mut per = bsp::init(); // Configure clock gates per.rcc.ahbenr.modify(|_, w| { // enable IO Port A (push-button) w.iopaen().enabled(); // enable IO Pord D (piezo) w.iopden().enabled(); // enable IO Port E (LEDs) w.iopeen().enabled(); // enable ADC 1/2 w.adc12en().enabled() }); // Clock ADC1/2 from HCLK. // NB: I don't understand why this is necessary; the RCC should already be generating a clock // as per above? per.adc1_2.ccr.modify(|_, w| { unsafe {w.ckmode().bits(0b11) } }); // All LEDS are outputs per.gpioe.moder.modify(|_, w| { w.moder8().output(); w.moder9().output(); w.moder10().output(); w.moder11().output(); w.moder12().output(); w.moder13().output(); w.moder14().output(); w.moder15().output() }); // Configure push-button (PA0) as input; // Configure PA1 as analog-in per.gpioa.moder.modify(|_, w| { w.moder0().input() .moder1().analog() }); // Clear PA3. PA3 can then be a ground reference point for the piezo. // per.gpioa.odr.write(|w| { // w.odr3().clear_bit() // }); // Configure piezo as digital input per.gpiod.moder.modify(|_, w| { w.moder0().input() }); // Turn on all the LEDs in the compass per.gpioe.odr.write(|w| { w.odr8().set_bit(); w.odr9().set_bit(); w.odr10().set_bit(); w.odr11().set_bit(); w.odr12().set_bit(); w.odr13().set_bit(); w.odr14().set_bit(); w.odr15().set_bit() }); // Enable the ADC voltage regulator. 15.3.6 // Note: by default ADC will be clocked off the bus clock, divided by two. per.adc1.cr.modify(|_, w| { w.deeppwd().clear_bit() }); // Docs make it sound like this _must_ be two separate writes. // "T ADCVREG_STUP // "The software must wait for the startup time of the ADC voltage regulator // (T ADCVREG_STUP ) before launching a calibration or enabling the ADC." // 10 uS worst-case delay(1000); // >= 10 us per.adc1.cr.modify(|_, w| { w.advregen().set_bit() }); delay(1000); // >= 10 us // ADC CALIBRATION (15.3.8) // 1. set ADCALDIF=0 (default) for single-ended cal per.adc1.cr.modify(|_, w| { w.adcaldif().clear_bit() }); // Start cal per.adc1.cr.modify(|_, w| { w.adcal().set_bit() }); delay(1000); // >= 10 us //panic!("adc1.isr: {:x}\n adc1.cr: {:x}", // per.adc1.isr.read().bits(), // per.adc1.cr.read().bits()); // Wait for done while per.adc1.cr.read().adcal().bit() { } // Enable ADC (15.3.9) per.adc1.cr.modify(|_, w| { w.aden().set_bit() }); // wait for ADRDY=1 while per.adc1.isr.read().adrdy().bit_is_clear() { } // Configure sample time (15.3.12) // 0b000 = 1.5 clocks // 0b001 = 2.5 clocks // 0b010 = 4.5 clocks // 0b011 = 7.5 clocks // 0b100 = 19.5 clocks // 0b101 = 61.5 clocks // 0b110 = 181.5 clocks // 0b111 = 601.5 clocks per.adc1.smpr1.modify(|_, w| { unsafe { w.smp1().bits(0b010) }; unsafe { w.smp2().bits(0b010) }; unsafe { w.smp3().bits(0b010) }; unsafe { w.smp4().bits(0b010) }; unsafe { w.smp5().bits(0b010) }; unsafe { w.smp6().bits(0b010) }; unsafe { w.smp7().bits(0b010) }; unsafe { w.smp8().bits(0b010) }; unsafe { w.smp9().bits(0b010) }; w }); // Configure mux'ing: SQRx (regular conversion), JSQRx (injected conversion; don't use these) // Draw from ADC1_IN2 -- PA1 per.adc1.sqr1.modify(|_, w| { unsafe { w.sq1().bits(2) } }); // Enable continuous mode (i.e. data will just continually appear in DR) per.adc1.cfgr.modify(|_, w| { w.cont().set_bit() }); // Start ADC! per.adc1.cr.modify(|_, w| { w.adstart().set_bit() }); bkpt(); // We have 48 KBytes of ram // But sizes >= 4096 seem to cause faults :s // let mut buffer = [0u8; 1024]; // let mut background_level = 0; // 0 .. 65536 // loop { // for sample in &mut buffer[..256] { // *sample = (per.adc1.dr.read().bits() >> 4) as u8; // } // let bg_this_iter = buffer[..256].iter().map(|&x| x as u32).sum::(); // if background_level == 0 { // background_level = bg_this_iter; // } // let thresh = background_level / 230; // if buffer[..256].iter().any(|&s| s as u32 > thresh) { // for sample in &mut buffer[256..] { // *sample = (per.adc1.dr.read().bits() >> 4) as u8; // } // itm::write_all(&mut per.itm.stim[0], &buffer); // //itm::write_all(&mut per.itm.stim[0], &buffer[..1024]); // //delay(8000000); // //itm::write_all(&mut per.itm.stim[0], &buffer[1024..2048]); // //delay(8000000); // //itm::write_all(&mut per.itm.stim[0], &buffer[2048..3072]); // //delay(8000000); // //itm::write_all(&mut per.itm.stim[0], &buffer[3072..]); // //delay(8000000); // } // background_level = (background_level*7 + bg_this_iter) / 8; // LPF // } // continuous dump mode (8-bit): // loop { // let sample = (per.adc1.dr.read().bits() >> 4) as u8; // itm::write_all(&mut per.itm.stim[0], &[sample]); // } loop { // median filter decimation // NB: 3.6 KB/sec for 63 filter width // 4.4 KB/sec for 53 filter width // 4.6 KB/sec for 51 filter width // 5.0 KB/sec for 49 filter width vv--- these rates and below show alias patterns // 5.0 KB/sec for 47 filter width // 5.0 KB/sec for 31 filter width // 5.0 KB/sec for 15 filter width // measured: ll /tmp/itm.txt ; sleep 10 ; ll /tmp/itm.txt let mut buf = [0; 51]; for sub_sample in buf.iter_mut() { *sub_sample = (per.adc1.dr.read().bits() << 4) as u16; } pdqsort::sort(&mut buf); let sample = buf[25]; let high = (sample >> 8) as u8; let low = (sample & 0xff) as u8; itm::write_all(&mut per.itm.stim[0], &[low, high]); } }