diff --git a/boards/feather_m0/Cargo.toml b/boards/feather_m0/Cargo.toml index 81afb47d90b0..85819e374b0f 100644 --- a/boards/feather_m0/Cargo.toml +++ b/boards/feather_m0/Cargo.toml @@ -39,7 +39,7 @@ version = "0.3.1" [dependencies.embedded-sdmmc] optional = true -version = "0.3" +version = "0.9" [dev-dependencies] cortex-m = "0.7" @@ -49,8 +49,9 @@ defmt-rtt = "0.4" drogue-nom-utils = "0.1" embassy-executor = {version = "0.6.2", features = ["arch-cortex-m", "executor-thread", "task-arena-size-64"]} embedded-graphics = "0.7.1" +embedded-hal-bus = "0.3.0" heapless = "0.8" -nom = {version = "5", default-features = false} +nom = {version = "8", default-features = false} panic-halt = "0.2" panic-probe = "0.3" panic-semihosting = "0.6" diff --git a/boards/feather_m0/examples/adalogger.rs b/boards/feather_m0/examples/adalogger.rs index 13dfecc89df8..9681db89f5a0 100644 --- a/boards/feather_m0/examples/adalogger.rs +++ b/boards/feather_m0/examples/adalogger.rs @@ -1,17 +1,18 @@ #![no_std] #![no_main] +#![allow(static_mut_refs)] #[cfg(not(feature = "use_semihosting"))] use panic_halt as _; #[cfg(feature = "use_semihosting")] use panic_semihosting as _; -use core::fmt::Write; +use core::cell::OnceCell; use core::sync::atomic; -use cortex_m::interrupt::free as disable_interrupts; use cortex_m::peripheral::NVIC; -use embedded_sdmmc::{Controller, SdMmcSpi, VolumeIdx}; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_sdmmc::{SdCard, VolumeIdx, VolumeManager}; use usb_device::bus::UsbBusAllocator; use usb_device::prelude::*; use usbd_serial::{SerialPort, USB_CLASS_CDC}; @@ -26,10 +27,9 @@ use hal::fugit::RateExtU32; use hal::pac::{interrupt, CorePeripherals, Peripherals}; use hal::prelude::*; use hal::rtc; +use hal::timer::TimerCounter; use hal::usb::UsbBus; -use heapless::String; - #[entry] fn main() -> ! { // setup basic peripherals @@ -48,8 +48,21 @@ fn main() -> ! { let timer_clock = clocks .configure_gclk_divider_and_source(ClockGenId::Gclk3, 32, ClockSource::Osc32k, true) .unwrap(); + + // Setup timer clock from + let tc45 = &clocks.tc4_tc5(&timer_clock).unwrap(); + + // Setup a timer for the SPI driver delay source + let timer_spi = TimerCounter::tc4_(tc45, peripherals.tc4, &mut peripherals.pm); + + // Setup a timer for the SPI driver delay source + let timer_sdcard = TimerCounter::tc5_(tc45, peripherals.tc5, &mut peripherals.pm); + + // Setup the RTC for the SD card volume time source let rtc_clock = clocks.rtc(&timer_clock).unwrap(); - let timer = rtc::Rtc::clock_mode(peripherals.rtc, rtc_clock.freq(), &mut peripherals.pm); + let rtc = rtc::Rtc::clock_mode(peripherals.rtc, rtc_clock.freq(), &mut peripherals.pm); + + // Setup red led let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); @@ -57,19 +70,19 @@ fn main() -> ! { delay.delay_ms(500_u32); let bus_allocator = unsafe { - USB_ALLOCATOR = Some(bsp::usb_allocator( + let _ = USB_ALLOCATOR.set(bsp::usb_allocator( peripherals.usb, &mut clocks, &mut peripherals.pm, pins.usb_dm, pins.usb_dp, )); - USB_ALLOCATOR.as_ref().unwrap() + USB_ALLOCATOR.get().unwrap() }; unsafe { - USB_SERIAL = Some(SerialPort::new(bus_allocator)); - USB_BUS = Some( + let _ = USB_SERIAL.set(SerialPort::new(bus_allocator)); + let _ = USB_BUS.set( UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) .strings(&[StringDescriptors::new(LangID::EN) .manufacturer("Fake company") @@ -79,9 +92,8 @@ fn main() -> ! { .device_class(USB_CLASS_CDC) .build(), ); - } - unsafe { + // Enable USB interrupt core.NVIC.set_priority(interrupt::USB, 1); NVIC::unmask(interrupt::USB); } @@ -91,9 +103,9 @@ fn main() -> ! { // Now work on the SD peripherals. Slow SPI speed required on init let spi_sercom = periph_alias!(peripherals.spi_sercom); - let spi = bsp::spi_master( + let spi_bus = bsp::spi_master( &mut clocks, - 400_u32.kHz(), + 4.MHz(), spi_sercom, &mut peripherals.pm, pins.sclk, @@ -117,87 +129,134 @@ fn main() -> ! { } if sd_cd.is_low().unwrap() { - usbserial_write!("No card detected. Waiting...\r\n"); + serial_writeln!("No card detected. Waiting..."); while sd_cd.is_low().unwrap() { delay.delay_ms(250_u32); } } - usbserial_write!("Card inserted!\r\n"); + serial_writeln!("Card inserted!"); delay.delay_ms(250_u32); - let mut controller = Controller::new(SdMmcSpi::new(spi, sd_cs), timer); - - match controller.device().init() { - Ok(_) => { - // speed up SPI and read out some info - controller - .device() - .spi() - .reconfigure(|c| c.set_baud(4.MHz())); - usbserial_write!("OK!\r\nCard size...\r\n"); - match controller.device().card_size_bytes() { - Ok(size) => usbserial_write!("{} bytes\r\n", size), - Err(e) => usbserial_write!("Err: {:?}\r\n", e), - } + let spi_device = ExclusiveDevice::new(spi_bus, sd_cs, timer_spi).unwrap(); + let sdcard = SdCard::new(spi_device, timer_sdcard); + + match sdcard.num_bytes() { + Ok(num_bytes) => { + serial_writeln!("OK!\r\nCard size..."); + serial_writeln!("{} bytes", num_bytes); + + let volume_manager = VolumeManager::new(sdcard, rtc); for i in 0..=3 { - let volume = controller.get_volume(VolumeIdx(i)); - usbserial_write!("volume {:?}\r\n", volume); + let volume = volume_manager.open_volume(VolumeIdx(i)); + serial_writeln!("volume {:?}", volume); if let Ok(volume) = volume { - let root_dir = controller.open_root_dir(&volume).unwrap(); - usbserial_write!("Listing root directory:\r\n"); - controller - .iterate_dir(&volume, &root_dir, |x| { - usbserial_write!("\tFound: {:?}\r\n", x); - }) - .unwrap(); + match volume.open_root_dir() { + Ok(root_dir) => { + serial_writeln!("Listing root directory:"); + root_dir + .iterate_dir(|x| { + serial_writeln!("\tFound: {:?}", x); + }) + .unwrap(); + } + Err(e) => serial_writeln!("Directory err: {:?}!", e), + } } } } - Err(e) => usbserial_write!("Init err: {:?}!\r\n", e), + Err(e) => serial_writeln!("Init err: {:?}!", e), } - usbserial_write!("Done!\r\n"); + serial_writeln!("Done!"); loop { delay.delay_ms(1_000_u32); red_led.toggle().unwrap(); } } +static mut USB_ALLOCATOR: OnceCell> = OnceCell::new(); +static mut USB_BUS: OnceCell> = OnceCell::new(); +static mut USB_SERIAL: OnceCell> = OnceCell::new(); +static USB_DATA_RECEIVED: atomic::AtomicBool = atomic::AtomicBool::new(false); + +/// Borrows the global singleton `UsbSerial` for a brief period with interrupts +/// disabled +/// +/// # Arguments +/// `borrower`: The closure that gets run borrowing the global `UsbSerial` +/// +/// # Safety +/// the global singleton `UsbSerial` can be safely borrowed because we disable +/// interrupts while it is being borrowed, guaranteeing that interrupt handlers +/// like `USB` cannot mutate `UsbSerial` while we are as well. +/// +/// # Panic +/// If `init` has not been called and we haven't initialized our global +/// singleton `UsbSerial`, we will panic. +fn usbserial_get(borrower: T) -> R +where + T: Fn(&mut SerialPort) -> R, +{ + usb_free(|_| unsafe { + let usb_serial = USB_SERIAL.get_mut().expect("UsbSerial not initialized"); + borrower(usb_serial) + }) +} + +/// Execute closure `f` in an interrupt-free context. +/// +/// This as also known as a "critical section". +#[inline] +fn usb_free(f: F) -> R +where + F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R, +{ + NVIC::mask(interrupt::USB); + + let r = f(&unsafe { cortex_m::interrupt::CriticalSection::new() }); + + unsafe { + NVIC::unmask(interrupt::USB); + }; + + r +} + /// Writes the given message out over USB serial. +/// +/// # Arguments +/// * println args: variable arguments passed along to `core::write!` +/// +/// # Warning +/// as this function deals with a static mut, and it is also accessed in the +/// USB interrupt handler, we both have unsafe code for unwrapping a static mut +/// as well as disabling of interrupts while we do so. +/// +/// # Safety +/// the only time the static mut is used, we have interrupts disabled so we know +/// we have sole access #[macro_export] -macro_rules! usbserial_write { - ($($tt:tt)*) => {{ - let mut s: String<1024> = String::new(); - write!(s, $($tt)*).unwrap(); - let message_bytes = s.as_bytes(); - let mut total_written = 0; - while total_written < message_bytes.len() { - let bytes_written = disable_interrupts(|_| unsafe { - match USB_SERIAL.as_mut().unwrap().write( - &message_bytes[total_written..] - ) { - Ok(count) => count, - Err(_) => 0, - } - }); - total_written += bytes_written; - } +macro_rules! serial_writeln { + ($($tt:tt)+) => {{ + use core::fmt::Write; + + let mut s: heapless::String<256> = heapless::String::new(); + core::write!(&mut s, $($tt)*).unwrap(); + usbserial_get(|usbserial| { + usbserial.write(s.as_bytes()).ok(); + usbserial.write("\r\n".as_bytes()).ok(); + }); }}; } -static mut USB_ALLOCATOR: Option> = None; -static mut USB_BUS: Option> = None; -static mut USB_SERIAL: Option> = None; -static USB_DATA_RECEIVED: atomic::AtomicBool = atomic::AtomicBool::new(false); - #[interrupt] fn USB() { unsafe { - if let Some(usb_dev) = USB_BUS.as_mut() { - if let Some(serial) = USB_SERIAL.as_mut() { + if let Some(usb_dev) = USB_BUS.get_mut() { + if let Some(serial) = USB_SERIAL.get_mut() { usb_dev.poll(&mut [serial]); - let mut buf = [0u8; 16]; + let mut buf = [0u8; 64]; if let Ok(count) = serial.read(&mut buf) { if count > 0 { USB_DATA_RECEIVED.store(true, atomic::Ordering::Relaxed); diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 6194e9d26b28..69e4c4ad725e 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, + adc::{Accumulation, Prescaler}, clock::GenericClockController, }; @@ -46,8 +46,8 @@ fn main() -> ! { let mut adc_pin = pins.a0.into_alternate(); loop { - let res = adc.read(&mut adc_pin); + let _res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", read).unwrap(); + cortex_m_semihosting::hprintln!("ADC value: {}", _res).unwrap(); } } diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs index 56139256ba01..a2b626b01eea 100644 --- a/boards/feather_m0/examples/async_adc.rs +++ b/boards/feather_m0/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, + adc::{Accumulation, Adc0, Prescaler}, clock::GenericClockController, }; diff --git a/boards/feather_m0/examples/clock.rs b/boards/feather_m0/examples/clock.rs index f32db4f23b5c..58f362b116f7 100644 --- a/boards/feather_m0/examples/clock.rs +++ b/boards/feather_m0/examples/clock.rs @@ -1,16 +1,15 @@ #![no_std] #![no_main] - -use core::fmt::Write; +#![allow(static_mut_refs)] #[cfg(not(feature = "use_semihosting"))] use panic_halt as _; #[cfg(feature = "use_semihosting")] use panic_semihosting as _; -use cortex_m::interrupt::free as disable_interrupts; +use core::cell::OnceCell; +use core::sync::atomic; use cortex_m::peripheral::NVIC; -use heapless::String; use usb_device::bus::UsbBusAllocator; use usb_device::prelude::*; use usbd_serial::{SerialPort, USB_CLASS_CDC}; @@ -52,23 +51,23 @@ fn main() -> ! { let rtc = rtc::Rtc::clock_mode(peripherals.rtc, rtc_clock.freq(), &mut peripherals.pm); unsafe { - RTC = Some(rtc); + let _ = RTC.set(rtc); } - // initialize USB let bus_allocator = unsafe { - USB_ALLOCATOR = Some(bsp::usb_allocator( + let _ = USB_ALLOCATOR.set(bsp::usb_allocator( peripherals.usb, &mut clocks, &mut peripherals.pm, pins.usb_dm, pins.usb_dp, )); - USB_ALLOCATOR.as_ref().unwrap() + USB_ALLOCATOR.get().unwrap() }; + unsafe { - USB_SERIAL = Some(SerialPort::new(bus_allocator)); - USB_BUS = Some( + let _ = USB_SERIAL.set(SerialPort::new(bus_allocator)); + let _ = USB_BUS.set( UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) .strings(&[StringDescriptors::new(LangID::EN) .manufacturer("Fake company") @@ -78,74 +77,132 @@ fn main() -> ! { .device_class(USB_CLASS_CDC) .build(), ); - } - unsafe { + + // Enable USB interrupt core.NVIC.set_priority(interrupt::USB, 1); NVIC::unmask(interrupt::USB); } + // Wait to receive something + while !USB_DATA_RECEIVED.load(atomic::Ordering::Relaxed) { + delay.delay_ms(250_u32); + red_led.toggle().unwrap(); + } + red_led.set_high().unwrap(); + // Print the time forever! loop { - red_led.toggle().ok(); - let time = - disable_interrupts(|_| unsafe { RTC.as_mut().map(|rtc| rtc.current_time()) }).unwrap(); - - let mut data = String::<16>::new(); - write!( - data, - "{:02}:{:02}:{:02}\r\n", - time.hours, time.minutes, time.seconds - ) - .ok() - .unwrap(); - write_serial(data.as_bytes()); + let time = usb_free(|_| unsafe { RTC.get_mut().map(|rtc| rtc.current_time()) }).unwrap(); + + serial_writeln!("{:02}:{:02}:{:02}", time.hours, time.minutes, time.seconds); delay.delay_ms(1_000_u32); } } -static mut USB_ALLOCATOR: Option> = None; -static mut USB_BUS: Option> = None; -static mut USB_SERIAL: Option> = None; -static mut RTC: Option> = None; +static mut USB_ALLOCATOR: OnceCell> = OnceCell::new(); +static mut USB_BUS: OnceCell> = OnceCell::new(); +static mut USB_SERIAL: OnceCell> = OnceCell::new(); +static USB_DATA_RECEIVED: atomic::AtomicBool = atomic::AtomicBool::new(false); +static mut RTC: OnceCell> = OnceCell::new(); + +/// Borrows the global singleton `UsbSerial` for a brief period with interrupts +/// disabled +/// +/// # Arguments +/// `borrower`: The closure that gets run borrowing the global `UsbSerial` +/// +/// # Safety +/// the global singleton `UsbSerial` can be safely borrowed because we disable +/// interrupts while it is being borrowed, guaranteeing that interrupt handlers +/// like `USB` cannot mutate `UsbSerial` while we are as well. +/// +/// # Panic +/// If `init` has not been called and we haven't initialized our global +/// singleton `UsbSerial`, we will panic. +fn usbserial_get(borrower: T) -> R +where + T: Fn(&mut SerialPort) -> R, +{ + usb_free(|_| unsafe { + let usb_serial = USB_SERIAL.get_mut().expect("UsbSerial not initialized"); + borrower(usb_serial) + }) +} + +/// Execute closure `f` in an interrupt-free context. +/// +/// This as also known as a "critical section". +#[inline] +fn usb_free(f: F) -> R +where + F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R, +{ + NVIC::mask(interrupt::USB); + + let r = f(&unsafe { cortex_m::interrupt::CriticalSection::new() }); -fn write_serial(bytes: &[u8]) { unsafe { - if let Some(serial) = USB_SERIAL.as_mut() { - serial.write(bytes).unwrap(); - }; - } + NVIC::unmask(interrupt::USB); + }; + + r +} + +/// Writes the given message out over USB serial. +/// +/// # Arguments +/// * println args: variable arguments passed along to `core::write!` +/// +/// # Warning +/// as this function deals with a static mut, and it is also accessed in the +/// USB interrupt handler, we both have unsafe code for unwrapping a static mut +/// as well as disabling of interrupts while we do so. +/// +/// # Safety +/// the only time the static mut is used, we have interrupts disabled so we know +/// we have sole access +#[macro_export] +macro_rules! serial_writeln { + ($($tt:tt)+) => {{ + use core::fmt::Write; + + let mut s: heapless::String<256> = heapless::String::new(); + core::write!(&mut s, $($tt)*).unwrap(); + usbserial_get(|usbserial| { + usbserial.write(s.as_bytes()).ok(); + usbserial.write("\r\n".as_bytes()).ok(); + }); + }}; } +#[inline] fn poll_usb() { unsafe { - if let Some(usb_dev) = USB_BUS.as_mut() { - if let Some(serial) = USB_SERIAL.as_mut() { + if let Some(usb_dev) = USB_BUS.get_mut() { + if let Some(serial) = USB_SERIAL.get_mut() { usb_dev.poll(&mut [serial]); - let mut buf = [0u8; 32]; + let mut buf = [0u8; 64]; if let Ok(count) = serial.read(&mut buf) { let mut buffer: &[u8] = &buf[..count]; - // echo to terminal - serial.write(buffer).unwrap(); + + if buffer.len() > 0 { + // echo to terminal + serial.write(buffer).unwrap(); + + USB_DATA_RECEIVED.store(true, atomic::Ordering::Relaxed); + } // Look for setting of time while buffer.len() > 5 { - match timespec(buffer) { + match Time::parse(core::str::from_utf8(buffer).unwrap()) { Ok((remaining, time)) => { - buffer = remaining; - disable_interrupts(|_| { - if let Some(rtc) = RTC.as_mut() { - rtc.set_time(rtc::Datetime { - seconds: time.second as u8, - minutes: time.minute as u8, - hours: time.hour as u8, - day: 0, - month: 0, - year: 0, - }); - }; - }); + buffer = remaining.as_bytes(); + + if let Some(rtc) = RTC.get_mut() { + rtc.set_time(time.into()); + }; } _ => break, }; @@ -161,29 +218,55 @@ fn USB() { poll_usb(); } -#[derive(Clone)] +#[derive(Debug)] pub struct Time { - hour: usize, - minute: usize, - second: usize, + hour: u8, + minute: u8, + second: u8, +} +impl Into for Time { + fn into(self) -> rtc::Datetime { + rtc::Datetime { + seconds: self.second, + minutes: self.minute, + hours: self.hour, + day: 0, + month: 0, + year: 0, + } + } } -use drogue_nom_utils::parse_usize; -use nom::{char, do_parse, named, opt, tag}; - -named!( - pub timespec