Clock works with +-10ms Jitter
This commit is contained in:
parent
1d063adee0
commit
f005c5c178
|
@ -15,7 +15,9 @@ defmt = "~0.2.0"
|
||||||
defmt-rtt = "~0.2.0"
|
defmt-rtt = "~0.2.0"
|
||||||
panic-probe = { version = "~0.2.0", features = ["print-defmt"] }
|
panic-probe = { version = "~0.2.0", features = ["print-defmt"] }
|
||||||
stm32f1xx-hal = { version = "~0.6.1", features = ["stm32f103", "rt"] }
|
stm32f1xx-hal = { version = "~0.6.1", features = ["stm32f103", "rt"] }
|
||||||
embedded-hal = {version = "~0.2.3", feature = ["unproven"]}
|
embedded-hal = {version = "~0.2.3"}
|
||||||
|
nb = "~1.0.0"
|
||||||
|
nmea0183 = "~0.2.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# set logging levels here
|
# set logging levels here
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
use nmea0183::{ParseResult, Parser};
|
||||||
|
use stm32f1xx_hal::{
|
||||||
|
delay::Delay,
|
||||||
|
prelude::*,
|
||||||
|
serial::{Config, Serial},
|
||||||
|
stm32,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::application::App;
|
||||||
|
use crate::time;
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn poll_gps(&mut self) -> () {
|
||||||
|
match self.serial.read() {
|
||||||
|
Ok(byte) => {
|
||||||
|
self.board_led.toggle().unwrap();
|
||||||
|
if let Some(result) = self.gps_parser.parse_from_byte(byte) {
|
||||||
|
match result {
|
||||||
|
Ok(ParseResult::GGA(Some(gga))) => {
|
||||||
|
time::set(&gga.time);
|
||||||
|
defmt::info!("Got GGA")
|
||||||
|
} // Got GGA sentence
|
||||||
|
Ok(_) => {} // Some other sentences..
|
||||||
|
Err(_) => {} // Got parse error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => {}
|
||||||
|
Err(nb::Error::Other(_)) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,11 +10,14 @@ use stm32f1xx_hal::{
|
||||||
stm32,
|
stm32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod gps;
|
||||||
mod setup;
|
mod setup;
|
||||||
|
|
||||||
//use crate::exit;
|
//use crate::exit;
|
||||||
pub use setup::setup;
|
pub use setup::setup;
|
||||||
|
|
||||||
|
use crate::time;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
delay: Delay,
|
delay: Delay,
|
||||||
board_led: gpioc::PC13<Output<PushPull>>,
|
board_led: gpioc::PC13<Output<PushPull>>,
|
||||||
|
@ -25,29 +28,17 @@ pub struct App {
|
||||||
gpiob::PB11<Input<Floating>>,
|
gpiob::PB11<Input<Floating>>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
|
gps_parser: Parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn run(mut self) -> ! {
|
pub fn run(mut self) -> ! {
|
||||||
defmt::info!("Application Startup!");
|
defmt::info!("Application Startup!");
|
||||||
|
|
||||||
let mut parser = Parser::new();
|
let mut last_ts = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.serial.read() {
|
self.poll_gps();
|
||||||
Ok(byte) => {
|
|
||||||
self.board_led.toggle().unwrap();
|
|
||||||
if let Some(result) = parser.parse_from_byte(byte) {
|
|
||||||
match result {
|
|
||||||
Ok(ParseResult::GGA(Some(_))) => defmt::info!("Got GGA"), // Got GGA sentence
|
|
||||||
Ok(_) => {} // Some other sentences..
|
|
||||||
Err(_) => {} // Got parse error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(nb::Error::WouldBlock) => {}
|
|
||||||
Err(nb::Error::Other(_)) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//exit();
|
//exit();
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use stm32f1xx_hal::{
|
use stm32f1xx_hal::{
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
|
pac::Interrupt,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
serial::{Config, Serial},
|
serial::{Config, Serial},
|
||||||
stm32,
|
stm32,
|
||||||
|
timer::{Event, Timer},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use nmea0183::Parser;
|
||||||
|
|
||||||
use crate::application::App;
|
use crate::application::App;
|
||||||
|
use crate::time;
|
||||||
|
|
||||||
pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> App {
|
pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> App {
|
||||||
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
|
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
|
||||||
|
@ -52,9 +57,14 @@ pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> A
|
||||||
&mut rcc.apb1,
|
&mut rcc.apb1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let gps_parser = Parser::new();
|
||||||
|
|
||||||
|
time::setup(dp.TIM2, &clocks, &mut rcc.apb1);
|
||||||
|
|
||||||
App {
|
App {
|
||||||
delay,
|
delay,
|
||||||
board_led,
|
board_led,
|
||||||
serial,
|
serial,
|
||||||
|
gps_parser,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -1,13 +1,11 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
|
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
use stm32f1xx_hal as _;
|
use stm32f1xx_hal as _;
|
||||||
|
|
||||||
pub mod application;
|
pub mod application;
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||||
|
@ -16,12 +14,9 @@ fn panic() -> ! {
|
||||||
cortex_m::asm::udf()
|
cortex_m::asm::udf()
|
||||||
}
|
}
|
||||||
|
|
||||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
defmt::timestamp!("{=u32}", {
|
||||||
defmt::timestamp!("{=usize}", {
|
|
||||||
// NOTE(no-CAS) `timestamps` runs with interrupts disabled
|
// NOTE(no-CAS) `timestamps` runs with interrupts disabled
|
||||||
let n = COUNT.load(Ordering::Relaxed);
|
time::get_timestamp()
|
||||||
COUNT.store(n + 1, Ordering::Relaxed);
|
|
||||||
n
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Terminates the application and makes `probe-run` exit with exit-code = 0
|
/// Terminates the application and makes `probe-run` exit with exit-code = 0
|
||||||
|
|
|
@ -7,8 +7,6 @@ use wspr_beacon::application;
|
||||||
|
|
||||||
#[cortex_m_rt::entry]
|
#[cortex_m_rt::entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
defmt::info!("Hello, world!");
|
|
||||||
|
|
||||||
// Get access to the core peripherals from the cortex-m crate
|
// Get access to the core peripherals from the cortex-m crate
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
// Get access to the device specific peripherals from the peripheral access crate
|
// Get access to the device specific peripherals from the peripheral access crate
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use core::ops::DerefMut;
|
||||||
|
use cortex_m::interrupt::{free, Mutex};
|
||||||
|
use stm32f1xx_hal::{
|
||||||
|
delay::Delay,
|
||||||
|
pac::interrupt,
|
||||||
|
pac::{Interrupt, TIM2},
|
||||||
|
prelude::*,
|
||||||
|
rcc::{Clocks, APB1},
|
||||||
|
serial::{Config, Serial},
|
||||||
|
stm32,
|
||||||
|
timer::{CountDownTimer, Event, Timer},
|
||||||
|
};
|
||||||
|
|
||||||
|
use nmea0183::datetime::Time;
|
||||||
|
|
||||||
|
static TIME: Mutex<RefCell<Time>> = Mutex::new(RefCell::new(Time {
|
||||||
|
hours: 0,
|
||||||
|
minutes: 0,
|
||||||
|
seconds: 0.0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
static TIMER_TIM2: Mutex<RefCell<Option<CountDownTimer<TIM2>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
pub fn setup(tim2: TIM2, clocks: &Clocks, apb1: &mut APB1) {
|
||||||
|
let mut timer = Timer::tim2(tim2, clocks, apb1).start_count_down(1.khz());
|
||||||
|
// Generate an interrupt when the timer expires
|
||||||
|
timer.listen(Event::Update);
|
||||||
|
// Move the timer into our global storage
|
||||||
|
cortex_m::interrupt::free(|cs| *TIMER_TIM2.borrow(cs).borrow_mut() = Some(timer));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn TIM2() {
|
||||||
|
free(|cs| {
|
||||||
|
if let Some(ref mut tim2) = TIMER_TIM2.borrow(cs).borrow_mut().deref_mut() {
|
||||||
|
tim2.clear_update_interrupt_flag();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut time = TIME.borrow(cs).borrow_mut();
|
||||||
|
time.seconds += 0.001;
|
||||||
|
|
||||||
|
if time.seconds >= 60.0 {
|
||||||
|
time.seconds -= 60.0;
|
||||||
|
time.minutes += 1;
|
||||||
|
}
|
||||||
|
if time.minutes == 60 {
|
||||||
|
time.minutes = 0;
|
||||||
|
time.hours += 1;
|
||||||
|
}
|
||||||
|
if time.hours == 24 {
|
||||||
|
time.hours = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get() -> Time {
|
||||||
|
let mut res = Time {
|
||||||
|
hours: 0,
|
||||||
|
minutes: 0,
|
||||||
|
seconds: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
free(|cs| {
|
||||||
|
let time = TIME.borrow(cs).borrow();
|
||||||
|
res.seconds = time.seconds;
|
||||||
|
res.minutes = time.minutes;
|
||||||
|
res.hours = time.hours;
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(new_time: &Time) -> () {
|
||||||
|
free(|cs| {
|
||||||
|
let mut time = TIME.borrow(cs).borrow_mut();
|
||||||
|
let sec_delta = time.seconds - new_time.seconds;
|
||||||
|
if time.hours != new_time.hours
|
||||||
|
|| time.minutes != new_time.minutes
|
||||||
|
|| sec_delta > 0.05
|
||||||
|
|| sec_delta < -0.05
|
||||||
|
{
|
||||||
|
time.seconds = new_time.seconds;
|
||||||
|
time.minutes = new_time.minutes;
|
||||||
|
time.hours = new_time.hours;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_timestamp() -> u32 {
|
||||||
|
let mut res = 0;
|
||||||
|
free(|cs| {
|
||||||
|
let time = TIME.borrow(cs).borrow();
|
||||||
|
res = (time.seconds * 1000.0) as u32;
|
||||||
|
res += (time.minutes as u32) * 60 * 1000;
|
||||||
|
res += (time.hours as u32) * 60 * 60 * 1000;
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
Loading…
Reference in New Issue