From 36da28fc025bbcfc3743c85111544be46db30a30 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 20 Nov 2022 20:42:50 +0100 Subject: [PATCH] Complete rewrite using RTIC Stripped out tests for now --- .cargo/config.toml | 2 +- Cargo.toml | 45 +++--- src/application/gps.rs | 39 ----- src/application/mod.rs | 144 ------------------ src/application/setup.rs | 107 -------------- src/lib.rs | 208 -------------------------- src/main.rs | 310 +++++++++++++++++++++++++++++++++++++++ src/time.rs | 104 ------------- testsuite/Cargo.toml | 41 ------ testsuite/tests/loc.rs | 56 ------- testsuite/tests/wspr.rs | 33 ----- 11 files changed, 332 insertions(+), 757 deletions(-) delete mode 100644 src/application/gps.rs delete mode 100644 src/application/mod.rs delete mode 100644 src/application/setup.rs delete mode 100644 src/lib.rs create mode 100644 src/main.rs delete mode 100644 src/time.rs delete mode 100644 testsuite/Cargo.toml delete mode 100644 testsuite/tests/loc.rs delete mode 100644 testsuite/tests/wspr.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index bf8dd40..dae491b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) -runner = "probe-run --chip STM32F103CB" +runner = "probe-run --chip STM32F103C8" rustflags = [ "-C", "linker=flip-link", "-C", "link-arg=-Tlink.x", diff --git a/Cargo.toml b/Cargo.toml index 6f27af4..aecabf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,22 @@ [package] -# TODO(1) fix `authors` and `name` if you didn't use `cargo-generate` authors = ["LongHairedHacker "] name = "wspr-beacon" edition = "2018" version = "0.1.0" -[workspace] -members = ["testsuite"] - [dependencies] -cortex-m = "~0.7.1" -cortex-m-rt = "~0.6.13" -cortex-m-rtic = "1.0.0" -defmt = "~0.2.0" -defmt-rtt = "~0.2.0" -panic-probe = { version = "~0.2.0", features = ["print-defmt"] } -stm32f1xx-hal = { version = "~0.6.1", features = ["stm32f103", "rt"] } -embedded-hal = {version = "~0.2.3"} -nb = "~1.0.0" -nmea0183 = "~0.2.3" -arrayvec = {version = "~0.7.0", default-features = false} +cortex-m = "0.7.6" +cortex-m-rt = "0.7.2" +cortex-m-rtic = "1.1.3" +defmt = "0.3.2" +defmt-rtt = "0.3.2" +panic-probe = { version = "0.3.0", features = ["print-defmt"] } +stm32f1xx-hal = { version = "0.9.0", features = ["stm32f103", "rt"] } +embedded-hal = {version = "0.2.3"} +nb = "1.0.0" +nmea0183 = "0.3.0" +arrayvec = {version = "0.7.0", default-features = false} +systick-monotonic = "1.0.0" [features] @@ -43,8 +40,8 @@ codegen-units = 1 debug = 2 debug-assertions = true # <- incremental = false -opt-level = 3 # <- -overflow-checks = true # <- +opt-level = 'z' # <- +overflow-checks = true # <- # cargo test [profile.test] @@ -52,8 +49,8 @@ codegen-units = 1 debug = 2 debug-assertions = true # <- incremental = false -opt-level = 3 # <- -overflow-checks = true # <- +opt-level = 3 # <- +overflow-checks = true # <- # cargo build/run --release [profile.release] @@ -61,9 +58,9 @@ codegen-units = 1 debug = 2 debug-assertions = false # <- incremental = false -#lto = 'fat' -opt-level = 3 # <- -overflow-checks = false # <- +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- # cargo test --release [profile.bench] @@ -72,8 +69,8 @@ debug = 2 debug-assertions = false # <- incremental = false lto = 'fat' -opt-level = 3 # <- -overflow-checks = false # <- +opt-level = 3 # <- +overflow-checks = false # <- # uncomment this to switch from the crates.io version of defmt to its git version # check app-template's README for instructions diff --git a/src/application/gps.rs b/src/application/gps.rs deleted file mode 100644 index f21a551..0000000 --- a/src/application/gps.rs +++ /dev/null @@ -1,39 +0,0 @@ -use nmea0183::{ParseResult, Parser}; -use stm32f1xx_hal::{ - delay::Delay, - prelude::*, - serial::{Config, Serial}, - stm32, -}; - -use crate::application::App; -use crate::loc; -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))) => { - if !self.transmitting { - time::set(&gga.time); - self.locator = loc::locator_from_coordinates( - gga.latitude.as_f64(), - gga.longitude.as_f64(), - ); - defmt::info!("Got GGA. New locator: {}", self.locator.as_str()); - } - } - Ok(_) => {} // Some other sentences.. - Err(_) => {} // Got parse error - } - } - } - Err(nb::Error::WouldBlock) => {} - Err(nb::Error::Other(_)) => {} - } - } -} diff --git a/src/application/mod.rs b/src/application/mod.rs deleted file mode 100644 index 42f808d..0000000 --- a/src/application/mod.rs +++ /dev/null @@ -1,144 +0,0 @@ -use cortex_m::{asm::wfi, prelude::*}; -use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; -use nb::{self, block}; -use nmea0183::{ParseResult, Parser}; -use stm32f1xx_hal::{ - delay::Delay, - gpio::{gpiob, gpioc, Alternate, Floating, Input, OpenDrain, Output, PushPull}, - i2c::BlockingI2c, - pac::I2C1, - prelude::*, - serial::Serial, - stm32, -}; - -mod gps; -mod setup; - -//use crate::exit; -pub use setup::setup; - -use crate::si5153; -use crate::time; -use crate::wspr; - -pub struct App { - delay: Delay, - board_led: gpioc::PC13>, - serial: Serial< - stm32::USART3, - ( - gpiob::PB10>, - gpiob::PB11>, - ), - >, - i2c: BlockingI2c< - I2C1, - ( - gpiob::PB6>, - gpiob::PB7>, - ), - >, - gps_parser: Parser, - locator: arrayvec::ArrayString<6>, - transmitting: bool, -} - -// For PLL with 800MHz -const WSPR_SYMBOLS: [si5153::PllParams; 4] = [ - // 7040100.000000: 113 + 44687 / 70401 - // actual: 7040100.000000 - si5153::PllParams { - p1: 14033, - p2: 17455, - p3: 70401, - }, - // 7040101.464844: 113 + 447801 / 705503 - // actual: 7040101.464844 - si5153::PllParams { - p1: 14033, - p2: 172785, - p3: 705503, - }, - // 7040102.929688: 113 + 38515 / 60682 - // actual: 7040102.929688 - si5153::PllParams { - p1: 14033, - p2: 14678, - p3: 60682, - }, - // 7040104.394531: 113 + 123233 / 194166 - // actual: 7040104.394531 - si5153::PllParams { - p1: 14033, - p2: 46378, - p3: 194166, - }, -]; - -impl App { - pub fn run(mut self) -> ! { - let callsign = arrayvec::ArrayString::<6>::from("DL1SSK").unwrap(); - - defmt::info!("Application Startup!"); - - let mut si_pll = si5153::Si5153::new(&self.i2c); - si_pll.init(&mut self.i2c, 25000000, 800000000 - 55_000, 800000000); - si_pll.set_ms_source(&mut self.i2c, si5153::Multisynth::MS0, si5153::PLL::A); - - defmt::info!("PLL setup complete."); - - let mut symbol_start = time::get(); - let mut symbol_idx: usize = 0; - let mut message: [u8; 162] = [0; 162]; - - loop { - self.poll_gps(); - - if !self.transmitting { - let ts = time::get(); - if self.locator.len() > 0 - && ts.minutes % 2 == 0 - && ts.seconds > 1.0 - && ts.seconds < 2.0 - { - defmt::info!("Starting tranmission"); - self.transmitting = true; - message = wspr::encode_message(&callsign, &self.locator, 27); - symbol_start = ts; - symbol_idx = 0; - si_pll.write_synth_params( - &mut self.i2c, - si5153::Multisynth::MS0, - &WSPR_SYMBOLS[message[symbol_idx] as usize], - ); - si_pll.enable_ms_output(&mut self.i2c, si5153::Multisynth::MS0); - } - } else { - let ts = time::get(); - let delta_sec = (ts.seconds - symbol_start.seconds) - + (ts.minutes - symbol_start.minutes) as f32 * 60.0; - if delta_sec >= 0.683 { - symbol_start = ts; - symbol_idx += 1; - - if symbol_idx < 162 { - si_pll.write_synth_params( - &mut self.i2c, - si5153::Multisynth::MS0, - &WSPR_SYMBOLS[message[symbol_idx] as usize], - ); - defmt::info!("Tranmitting Symbol {}", symbol_idx); - } else { - si_pll.disable_ms_output(&mut self.i2c, si5153::Multisynth::MS0); - self.transmitting = false; - defmt::info!("Transmission ended"); - } - } - } - //wfi(); - } - - //exit(); - } -} diff --git a/src/application/setup.rs b/src/application/setup.rs deleted file mode 100644 index 4e84d43..0000000 --- a/src/application/setup.rs +++ /dev/null @@ -1,107 +0,0 @@ -use stm32f1xx_hal::{ - delay::Delay, - i2c, - pac::Interrupt, - prelude::*, - serial::{Config, Serial}, - stm32, - timer::{Event, Timer}, -}; - -use nmea0183::Parser; - -use crate::application::App; -use crate::time; - -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 - // HAL structs - let mut flash = dp.FLASH.constrain(); - let mut rcc = dp.RCC.constrain(); - - // Freeze the configuration of all the clocks in the system and store the frozen frequencies in - // `clocks` - let clocks = rcc - .cfgr - .use_hse(8.mhz()) - .sysclk(72.mhz()) - .pclk1(36.mhz()) - .freeze(&mut flash.acr); - - defmt::info!("Clock Setup done"); - - // Take ownership over the raw flash and rcc devices and convert them into the corresponding - // HAL structs - let mut flash = dp.FLASH.constrain(); - - let mut rcc = dp.RCC.constrain(); - - // Freeze the configuration of all the clocks in the system and store the frozen frequencies in - // `clocks` - let clocks = rcc - .cfgr - .use_hse(8.mhz()) - .sysclk(72.mhz()) - .pclk1(36.mhz()) - .freeze(&mut flash.acr); - - defmt::info!("Clock Setup done"); - - // Acquire the GPIOC peripheral - let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); - let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); - - let mut afio = dp.AFIO.constrain(&mut rcc.apb2); - - let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); - - let delay = Delay::new(cp.SYST, clocks); - - // USART3 - // Configure pb10 as a push_pull output, this will be the tx pin - let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); - // Take ownership over pb11 - let rx = gpiob.pb11; - - // Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of - // the registers are used to enable and configure the device. - let serial = Serial::usart3( - dp.USART3, - (tx, rx), - &mut afio.mapr, - Config::default().baudrate(9600.bps()), - clocks, - &mut rcc.apb1, - ); - - let gps_parser = Parser::new(); - - let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl); - let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl); - let mut i2c = i2c::BlockingI2c::i2c1( - dp.I2C1, - (scl, sda), - &mut afio.mapr, - i2c::Mode::Standard { - frequency: 400_000.hz(), - }, - clocks, - &mut rcc.apb1, - 5, - 1, - 5, - 5, - ); - - time::setup(dp.TIM2, &clocks, &mut rcc.apb1); - - App { - delay, - board_led, - serial, - i2c, - gps_parser, - locator: arrayvec::ArrayString::new(), - transmitting: false, - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 4d9e893..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,208 +0,0 @@ -#![no_main] -#![no_std] -use defmt_rtt as _; // global logger - -use panic_probe as _; -use stm32f1xx_hal as _; - -// 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 -#[defmt::panic_handler] -fn panic() -> ! { - cortex_m::asm::udf() -} - -use rtic::app; - -mod si5153; - -#[app(device = stm32f1xx_hal::pac)] -mod app { - use stm32f1xx_hal::{ - delay::Delay, - gpio::{gpiob, gpioc, Alternate, Floating, Input, OpenDrain, Output, PushPull}, - i2c, - i2c::BlockingI2c, - pac::I2C1, - pac::{self, Interrupt}, - prelude::*, - prelude::*, - serial::{Config, Serial}, - stm32, - timer::{Event, Timer}, - }; - - use nmea0183::{datetime::Time, ParseResult, Parser}; - - use crate::si5153::Si5153; - - type AppI2C1 = BlockingI2c< - I2C1, - ( - gpiob::PB6>, - gpiob::PB7>, - ), - >; - - #[shared] - struct Shared { - i2c: AppI2C1, - time: Time, - } - - #[local] - struct Local { - gps_parser: Parser, - usart: Serial< - stm32::USART3, - ( - gpiob::PB10>, - gpiob::PB11>, - ), - >, - board_led: gpioc::PC13>, - pll: Si5153, - } - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - // Get access to the core peripherals from the cortex-m crate - let cp = cortex_m::Peripherals::take().unwrap(); - // Get access to the device specific peripherals from the peripheral access crate - let dp = pac::Peripherals::take().unwrap(); - - // Take ownership over the raw flash and rcc devices and convert them into the corresponding - // HAL structs - let mut flash = dp.FLASH.constrain(); - - let mut rcc = dp.RCC.constrain(); - - // Freeze the configuration of all the clocks in the system and store the frozen frequencies in - // `clocks` - let clocks = rcc - .cfgr - .use_hse(8.mhz()) - .sysclk(72.mhz()) - .pclk1(36.mhz()) - .freeze(&mut flash.acr); - - defmt::info!("Clock Setup done"); - - let mut timer = Timer::tim2(dp.TIM2, &clocks, &mut rcc.apb1).start_count_down(1.khz()); - // Generate an interrupt when the timer expires - timer.listen(Event::Update); - - // Acquire the GPIOC peripheral - let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); - let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); - - let mut afio = dp.AFIO.constrain(&mut rcc.apb2); - - let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); - - // USART3 - // Configure pb10 as a push_pull output, this will be the tx pin - let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); - // Take ownership over pb11 - let rx = gpiob.pb11; - - // Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of - // the registers are used to enable and configure the device. - let usart = Serial::usart3( - dp.USART3, - (tx, rx), - &mut afio.mapr, - Config::default().baudrate(9600.bps()), - clocks, - &mut rcc.apb1, - ); - - let gps_parser = Parser::new(); - - let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl); - let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl); - let i2c = i2c::BlockingI2c::i2c1( - dp.I2C1, - (scl, sda), - &mut afio.mapr, - i2c::Mode::Standard { - frequency: 400_000.hz(), - }, - clocks, - &mut rcc.apb1, - 5, - 1, - 5, - 5, - ); - - let pll = Si5153::new(&i2c); - - let time = Time { - hours: 0, - minutes: 0, - seconds: 0.0, - }; - - ( - Shared { i2c, time }, - Local { - gps_parser, - usart, - board_led, - pll, - }, - init::Monotonics(), - ) - } - - #[task(binds = USART3, local=[usart, board_led, gps_parser], shared=[time])] - fn usart3(mut ctx: usart3::Context) { - match ctx.local.usart.read() { - Ok(byte) => { - ctx.local.board_led.toggle().unwrap(); - if let Some(result) = ctx.local.gps_parser.parse_from_byte(byte) { - match result { - Ok(ParseResult::GGA(Some(gga))) => { - ctx.shared.time.lock(|time| { - time.hours = gga.time.hours; - time.minutes = gga.time.minutes; - time.seconds = gga.time.seconds; - }); - /* - self.locator = loc::locator_from_coordinates( - gga.latitude.as_f64(), - gga.longitude.as_f64(), - ); - defmt::info!("Got GGA. New locator: {}", self.locator.as_str()); - */ - } - Ok(_) => {} // Some other sentences.. - Err(_) => {} // Got parse error - } - } - } - Err(nb::Error::WouldBlock) => {} - Err(nb::Error::Other(_)) => {} - } - } - - #[task(binds = TIM2, shared=[time])] - fn tim2(mut ctx: tim2::Context) { - ctx.shared.time.lock(|time| { - 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; - } - }); - } -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..066cc3c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,310 @@ +#![no_main] +#![no_std] +use defmt_rtt as _; // global logger + +use panic_probe as _; +use stm32f1xx_hal as _; + +// 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 +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +use rtic::app; + +mod loc; +mod si5153; +mod wspr; + +#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [TIM3])] +mod app { + + use stm32f1xx_hal::{ + gpio::{gpiob, gpioc, Alternate, Floating, Input, OpenDrain, Output, PushPull}, + i2c, + i2c::BlockingI2c, + pac::I2C1, + prelude::*, + serial::{Config, Serial}, + stm32, + timer::{self, Event}, + }; + + use systick_monotonic::Systick; + + use arrayvec::ArrayString; + + use nmea0183::{datetime::Time, ParseResult, Parser}; + + use crate::loc; + use crate::si5153; + use crate::wspr; + + type AppI2C1 = BlockingI2c< + I2C1, + ( + gpiob::PB6>, + gpiob::PB7>, + ), + >; + + #[monotonic(binds = SysTick, default = true)] + type MonoTimer = Systick<1000>; + + #[shared] + struct Shared { + time: Time, + locator: ArrayString<6>, + } + + #[local] + struct Local { + gps_parser: Parser, + usart: Serial< + stm32::USART3, + ( + gpiob::PB10>, + gpiob::PB11>, + ), + >, + pll: si5153::Si5153, + i2c: AppI2C1, + board_led: gpioc::PC13>, + transmitting: bool, + symbol_pos: usize, + message: [u8; 162], + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let mut flash = cx.device.FLASH.constrain(); + let rcc = cx.device.RCC.constrain(); + + // Freeze the configuration of all the clocks in the system and store the frozen frequencies in + // `clocks` + let clocks = rcc + .cfgr + .use_hse(8.MHz()) + .sysclk(72.MHz()) + .pclk1(36.MHz()) + .freeze(&mut flash.acr); + + defmt::info!("Clock Setup done"); + + let mono = Systick::new(cx.core.SYST, clocks.pclk1().to_Hz()); + + let mut afio = cx.device.AFIO.constrain(); + + let mut timer = timer::Timer2::new(cx.device.TIM2, &clocks).counter_hz(); + timer.start(1.kHz()).unwrap(); + // Generate an interrupt when the timer expires + timer.listen(Event::Update); + + // Acquire the GPIOC peripheral + let mut gpiob = cx.device.GPIOB.split(); + let mut gpioc = cx.device.GPIOC.split(); + + let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); + + // USART3 + // Configure pb10 as a push_pull output, this will be the tx pin + let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); + // Take ownership over pb11 + let rx = gpiob.pb11; + + // Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of + // the registers are used to enable and configure the device. + let usart = Serial::usart3( + cx.device.USART3, + (tx, rx), + &mut afio.mapr, + Config::default().baudrate(9600.bps()), + clocks, + ); + + let gps_parser = Parser::new(); + + let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl); + let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl); + let i2c = i2c::BlockingI2c::i2c1( + cx.device.I2C1, + (scl, sda), + &mut afio.mapr, + i2c::Mode::Standard { + frequency: 400.kHz(), + }, + clocks, + 5, + 1, + 5, + 5, + ); + + let pll = si5153::Si5153::new(&i2c); + + let time = Time { + hours: 0, + minutes: 0, + seconds: 0.0, + }; + + let locator = ArrayString::new(); + + transmit::spawn().unwrap(); + + ( + Shared { time, locator }, + Local { + gps_parser, + usart, + i2c, + pll, + board_led, + transmitting: false, + symbol_pos: 0, + message: [0; 162], + }, + init::Monotonics(mono), + ) + } + + const CALLSIGN: &str = "DL1SSk"; + const POWER: u8 = 37; + const WSPR_SYMBOL_LENGTH: u32 = 683; + + // For PLL with 800MHz + const WSPR_SYMBOLS: [si5153::PllParams; 4] = [ + // 7040100.000000: 113 + 44687 / 70401 + // actual: 7040100.000000 + si5153::PllParams { + p1: 14033, + p2: 17455, + p3: 70401, + }, + // 7040101.464844: 113 + 447801 / 705503 + // actual: 7040101.464844 + si5153::PllParams { + p1: 14033, + p2: 172785, + p3: 705503, + }, + // 7040102.929688: 113 + 38515 / 60682 + // actual: 7040102.929688 + si5153::PllParams { + p1: 14033, + p2: 14678, + p3: 60682, + }, + // 7040104.394531: 113 + 123233 / 194166 + // actual: 7040104.394531 + si5153::PllParams { + p1: 14033, + p2: 46378, + p3: 194166, + }, + ]; + + #[task(local=[pll, i2c, transmitting, symbol_pos, message], shared=[time, locator])] + fn transmit(mut ctx: transmit::Context) { + if !*ctx.local.transmitting { + let loc = ctx.shared.locator.lock(|l| l.clone()); + if loc.is_empty() { + transmit::spawn_after(10.secs().into()).unwrap(); + return; + } + + let time = ctx.shared.time.lock(|l| l.clone()); + if time.minutes % 2 != 0 || time.seconds < 1.0 || time.seconds > 2.0 { + transmit::spawn_after(100.millis().into()).unwrap(); + return; + } + + defmt::info!("Starting tranmission"); + let callsign = arrayvec::ArrayString::<6>::from(CALLSIGN).unwrap(); + *ctx.local.message = wspr::encode_message(&callsign, &loc, POWER); + *ctx.local.symbol_pos = 0; + *ctx.local.transmitting = true; + } + + let sym = ctx.local.message[*ctx.local.symbol_pos] as usize; + defmt::info!("transmitting symbol {} : {}", *ctx.local.symbol_pos, sym); + + ctx.local.pll.write_synth_params( + &mut ctx.local.i2c, + si5153::Multisynth::MS0, + &WSPR_SYMBOLS[sym], + ); + + if *ctx.local.symbol_pos == 0 { + ctx.local + .pll + .enable_ms_output(&mut ctx.local.i2c, si5153::Multisynth::MS0); + } + + if *ctx.local.symbol_pos < 162 { + *ctx.local.symbol_pos += 1; + transmit::spawn_after(WSPR_SYMBOL_LENGTH.millis().into()).unwrap(); + return; + } + + defmt::info!("Stopping transmission."); + ctx.local + .pll + .disable_ms_output(&mut ctx.local.i2c, si5153::Multisynth::MS0); + *ctx.local.transmitting = false; + transmit::spawn_after(100.millis().into()).unwrap(); + } + + #[task(binds = USART3, local=[usart, board_led, gps_parser], shared=[time, locator])] + fn usart3(mut ctx: usart3::Context) { + match ctx.local.usart.read() { + Ok(byte) => { + ctx.local.board_led.toggle(); + if let Some(result) = ctx.local.gps_parser.parse_from_byte(byte) { + match result { + Ok(ParseResult::GGA(Some(gga))) => { + ctx.shared.time.lock(|time| { + time.hours = gga.time.hours; + time.minutes = gga.time.minutes; + time.seconds = gga.time.seconds; + }); + + ctx.shared.locator.lock(|locator| { + *locator = loc::locator_from_coordinates( + gga.latitude.as_f64(), + gga.longitude.as_f64(), + ); + defmt::info!("Got GGA. New locator: {}", locator.as_str()); + }); + } + Ok(_) => {} // Some other sentences.. + Err(_) => {} // Got parse error + } + } + } + Err(nb::Error::WouldBlock) => {} + Err(nb::Error::Other(_)) => {} + } + } + + #[task(binds = TIM2, shared=[time])] + fn tim2(mut ctx: tim2::Context) { + ctx.shared.time.lock(|time| { + 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; + } + }); + } +} diff --git a/src/time.rs b/src/time.rs deleted file mode 100644 index c974f6d..0000000 --- a/src/time.rs +++ /dev/null @@ -1,104 +0,0 @@ -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> = Mutex::new(RefCell::new(Time { - hours: 0, - minutes: 0, - seconds: 0.0, -})); - -static TIMER_TIM2: Mutex>>> = 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; -} diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml deleted file mode 100644 index 1480538..0000000 --- a/testsuite/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -# TODO(1) fix `authors` if you didn't use `cargo-generate` -authors = ["LongHairedHacker "] -name = "testsuite" -publish = false -edition = "2018" -version = "0.1.0" - -[[test]] -name = "loc" -harness = false - -[[test]] -name = "wspr" -harness = false - -[dependencies] -wspr-beacon = { path = ".." } -cortex-m = "0.7.1" -cortex-m-rt = "0.6.12" -defmt = "0.2.0" -defmt-rtt = "0.2.0" -defmt-test = "0.2.0" -panic-probe = { version = "0.2.0", features = ["print-defmt"] } -arrayvec = {version = "~0.7.0", default-features = false} - -[features] -# set logging levels here -default = [ - # in tests, enable all logs - "defmt-trace", - # "dependency-a/defmt-trace", -] - -# do NOT modify these features -defmt-default = [] -defmt-trace = [] -defmt-debug = [] -defmt-info = [] -defmt-warn = [] -defmt-error = [] diff --git a/testsuite/tests/loc.rs b/testsuite/tests/loc.rs deleted file mode 100644 index ed53de0..0000000 --- a/testsuite/tests/loc.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![no_std] -#![no_main] - -use wspr_beacon as _; // memory layout + panic handler - -// See https://crates.io/crates/defmt-test/0.1.0 for more documentation (e.g. about the 'state' -// feature) -#[defmt_test::tests] -mod tests { - use arrayvec::ArrayString; - use defmt::{assert, assert_eq}; - use wspr_beacon::loc; - - #[test] - fn test_locations() { - assert_eq!( - loc::locator_from_coordinates(49.4395, 7.7635).as_str(), - "JN39vk" - ); - - assert_eq!( - loc::locator_from_coordinates(74.998306, 148.833363).as_str(), - "QQ44kx" - ); - - assert_eq!( - loc::locator_from_coordinates(64.701746, -18.640812).as_str(), - "IP04qq" - ); - - assert_eq!( - loc::locator_from_coordinates(19.251825, -81.541811).as_str(), - "EK99fg" - ); - - assert_eq!( - loc::locator_from_coordinates(-23.587708, 45.25882).as_str(), - "LG26pj" - ); - - assert_eq!( - loc::locator_from_coordinates(-77.566746, 167.875652).as_str(), - "RB32wk" - ); - - assert_eq!( - loc::locator_from_coordinates(-17.632622, -149.472404).as_str(), - "BH52gi" - ); - - assert_eq!( - loc::locator_from_coordinates(-19.77516, -43.959542).as_str(), - "GH80af" - ); - } -} diff --git a/testsuite/tests/wspr.rs b/testsuite/tests/wspr.rs deleted file mode 100644 index 87759fa..0000000 --- a/testsuite/tests/wspr.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![no_std] -#![no_main] - -use wspr_beacon as _; // memory layout + panic handler - -// See https://crates.io/crates/defmt-test/0.1.0 for more documentation (e.g. about the 'state' -// feature) -#[defmt_test::tests] -mod tests { - use arrayvec::ArrayString; - use defmt::{assert, assert_eq, Debug2Format}; - use wspr_beacon::wspr; - - #[test] - fn test_encoding() { - let call_sign = ArrayString::<6>::from(" K1ABC").unwrap(); - let loc = ArrayString::<6>::from("FN42").unwrap(); - let symbols = wspr::encode_message(&call_sign, &loc, 37); - - let expected_symbols: [u8; 162] = [ - 3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 2, 0, 1, 3, 1, 2, 2, 2, 1, 0, 0, 3, 2, 3, 1, 3, 3, 2, 2, - 0, 2, 0, 0, 0, 3, 2, 0, 1, 2, 3, 2, 2, 0, 0, 2, 2, 3, 2, 1, 1, 0, 2, 3, 3, 2, 1, 0, 2, - 2, 1, 3, 2, 1, 2, 2, 2, 0, 3, 3, 0, 3, 0, 3, 0, 1, 2, 1, 0, 2, 1, 2, 0, 3, 2, 1, 3, 2, - 0, 0, 3, 3, 2, 3, 0, 3, 2, 2, 0, 3, 0, 2, 0, 2, 0, 1, 0, 2, 3, 0, 2, 1, 1, 1, 2, 3, 3, - 0, 2, 3, 1, 2, 1, 2, 2, 2, 1, 3, 3, 2, 0, 0, 0, 0, 1, 0, 3, 2, 0, 1, 3, 2, 2, 2, 2, 2, - 0, 2, 3, 3, 2, 3, 2, 3, 3, 2, 0, 0, 3, 1, 2, 2, 2, - ]; - - for i in 0..162 { - assert_eq!(expected_symbols[i], symbols[i]); - } - } -}