#![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; } }); } }