322 lines
8.9 KiB
Rust
322 lines
8.9 KiB
Rust
#![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::<u32>();
|
|
// 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]);
|
|
}
|
|
}
|
|
|