Newer
Older
#![no_main]
#![no_std]
// use panic_halt as _;
use panic_semihosting as _;
#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [USART6])]
prelude::*,
rcc::{Clocks, Rcc},
serial::{self, Serial},
spi::{Mode, Phase, Polarity, Spi},
};
use half::f16;
use ham_cats::{
use crate::{
config::{Config, GpsSetting},
gps::{GpsModule, GpsPos},
const MODE: Mode = Mode {
polarity: Polarity::IdleLow,
phase: Phase::CaptureOnFirstTransition,
};
#[monotonic(binds = SysTick, default = true)]
usb_detect: gpio::Pin<'A', 5, gpio::Input>,
gps_enable: gpio::Pin<'C', 8, gpio::Output<gpio::OpenDrain>>,
red: gpio::Pin<'B', 14, gpio::Output>,
radio: Option<RadioManager<'static>>,
usb_dev: UsbDevice<'static, UsbBusType>,
shell: Shell,
}
fn setup_clocks(rcc: Rcc) -> Clocks {
rcc.cfgr
.use_hse(25.MHz())
.freeze()
}
#[init]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
let gpioa = dp.GPIOA.split();
let gpiob = dp.GPIOB.split();
// Setup flash
let mut flash = LockedFlash::new(dp.FLASH);
let config = Config::load_from_flash(&mut flash).unwrap_or_default();
// setup LEDs
let mut red = gpiob.pb14.into_push_pull_output();
let mut green = gpiob.pb15.into_push_pull_output();
// setup volt meter
let volt_pin = gpioc.pc0.into_analog();
let volt_adc = Adc::adc1(dp.ADC1, true, AdcConfig::default());
let volt_meter = VoltMeter::new(volt_pin, volt_adc);
let usb_detect = gpioa.pa5.into_pull_down_input();
// need to give time for the pull-down to take effect
let mut delay = dp.TIM5.delay_us(&clocks);
delay.delay_us(100u8);
let usb_connected = usb_detect.is_high();
let radio = if usb_connected {
None
} else {
let mosi = gpiob.pb5.into_alternate();
let miso = gpiob.pb4.into_alternate();
let sclk = gpiob.pb3.into_alternate();
let sdn = gpiob.pb12.into_push_pull_output();
let cs = gpioa.pa8.into_push_pull_output();
let spi = Spi::new(dp.SPI1, (sclk, miso, mosi), MODE, 10.MHz(), &clocks);
let buf = singleton!(: [u8; MAX_PACKET_LEN] = [0; MAX_PACKET_LEN]).unwrap();
let radio = RadioManager::new(spi, sdn, cs, delay, buf, &config);
// Setup GPS power
let mut gps_enable = gpioc.pc8.into_open_drain_output();
pins,
serial::Config::default()
.baudrate(9600.bps())
.wordlength_8(),
&clocks,
)
.unwrap()
.with_u8_data();
gps_serial.listen(serial::Event::Rxne);
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Setup USB
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
static mut USB_BUS: Option<usb_device::bus::UsbBusAllocator<UsbBusType>> = None;
let usb = USB {
usb_global: dp.OTG_FS_GLOBAL,
usb_device: dp.OTG_FS_DEVICE,
usb_pwrclk: dp.OTG_FS_PWRCLK,
pin_dm: gpioa.pa11.into(),
pin_dp: gpioa.pa12.into(),
hclk: clocks.hclk(),
};
unsafe {
USB_BUS.replace(UsbBus::new(usb, &mut EP_MEMORY));
}
let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() });
let usb_dev = UsbDeviceBuilder::new(
unsafe { USB_BUS.as_ref().unwrap() },
UsbVidPid(0x16c0, 0x27dd),
)
.manufacturer("cats.radio")
.product("CATS mobile transceiver")
.serial_number("4242")
.device_class(usbd_serial::USB_CLASS_CDC)
.build();
let shell = Shell::new(config.clone(), usb_serial);
let buf = cortex_m::singleton!(: [u8; MAX_PACKET_LEN] = [0; MAX_PACKET_LEN]).unwrap();
Status::Programming(false)
} else if radio.is_none() {
Status::Error(false)
} else if !config.enable_transmit {
green.set_high();
Status::Normal
};
if config.enable_transmit && radio.is_some() {
transmit_position::spawn().unwrap();
init::Monotonics(Systick::new(ctx.core.SYST, H_CLK)),
#[task(priority = 2, local = [], shared = [red, radio, config, status])]
fn radio_tick(mut ctx: radio_tick::Context) {
(ctx.shared.radio, ctx.shared.config).lock(|r, c| {
r.as_mut()
.unwrap()
.tick(&mut ctx.shared.red, Some((&c.callsign, c.ssid)))
.unwrap()
});
if ctx.shared.status.lock(|s| matches!(s, Status::Normal)) {
radio_tick::spawn_after(20u64.millis()).unwrap();
}
#[task(priority = 2, local = [buf], shared = [red, radio, gps, config, status])]
let (black_hole, transmit_period_seconds) =
config.lock(|c| (c.black_hole, c.transmit_period_seconds));
GpsSetting::Disabled => None,
GpsSetting::Fixed(lat, lon) => Some(GpsPos {
latitude: lat,
longitude: lon,
altitude_meters: 0.0,
speed_ms: 0.0,
true_course: 0.0,
}),
GpsSetting::Receiver => ctx.shared.gps.lock(|gps| gps.pos()),
};
let should_transmit = match pos {
Some(pos) => !black_hole.in_black_hole(pos.latitude, pos.longitude),
None => true,
};
if should_transmit {
let mut buf = [0; MAX_PACKET_LEN];
let mut cats = Packet::new(&mut buf);
config.lock(|config| {
cats.add_identification(ham_cats::whisker::Identification {
icon: config.icon,
callsign: config.callsign,
ssid: config.ssid,
})
.unwrap();
if !config.comment.is_empty() {
cats.add_comment(&config.comment).unwrap();
}
cats.add_route(Route::new(config.max_hops)).unwrap();
});
if let Some(pos) = pos {
cats.add_gps(Gps::new(
pos.latitude,
pos.longitude,
f16::from_f32(pos.altitude_meters),
0,
pos.true_course as f64,
f16::from_f32(pos.speed_ms),
))
.unwrap();
}
let buf = ctx.local.buf;
let mut x = Buffer::new_empty(buf);
cats.fully_encode(&mut x).unwrap();
.unwrap();
});
}
if ctx.shared.status.lock(|s| matches!(s, Status::Normal)) {
transmit_position::spawn_after(transmit_period_seconds.secs()).unwrap();
}
#[task(priority = 2, local = [green, usb_detect, gps_enable], shared = [red, status])]
fn led_handler(mut ctx: led_handler::Context) {
led_handler::spawn_after(LED_BLINK_RATE.millis()).unwrap();
let mut status = ctx.shared.status;
let cur_status = status.lock(|x| *x);
if ctx.local.usb_detect.is_high() && !matches!(cur_status, Status::Programming(_)) {
status.lock(|s| *s = Status::Programming(false));
ctx.shared.red.lock(|red| red.set_low());
Status::Normal => {}
Status::Programming(b) => {
ctx.local.green.set_state((*b).into());
*b = !*b;
}
Status::TxDisabled(b) => {
ctx.shared.red.lock(|red| red.set_state((*b).into()));
*b = !*b;
}
Status::Error(b) => {
ctx.shared.red.lock(|red| red.set_state((*b).into()));
ctx.local.green.set_state((!*b).into());
*b = !*b;
}
#[task(binds=USART2, priority = 3, shared=[gps], local=[gps_serial])]
fn gps_uart(mut ctx: gps_uart::Context) {
let gps_serial = ctx.local.gps_serial;
while let Ok(b) = gps_serial.read() {
ctx.shared.gps.lock(|gps| gps.consume(b))
}
}
#[task(binds=OTG_FS, priority = 2, local=[flash], shared=[usb_dev, shell, volt_meter])]
fn usb_fs(cx: usb_fs::Context) {
let usb_fs::SharedResources {
mut usb_dev,
mut shell,
} = cx.shared;
(&mut usb_dev, &mut shell).lock(|usb_dev, shell| {
while usb_dev.poll(&mut [shell.ushell.get_serial_mut()]) {