Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions boards/feather_m0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
191 changes: 125 additions & 66 deletions boards/feather_m0/examples/adalogger.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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
Expand All @@ -48,28 +48,41 @@ 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();

red_led.set_high().unwrap();
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")
Expand All @@ -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);
}
Expand All @@ -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,
Expand All @@ -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<UsbBusAllocator<UsbBus>> = OnceCell::new();
static mut USB_BUS: OnceCell<UsbDevice<UsbBus>> = OnceCell::new();
static mut USB_SERIAL: OnceCell<SerialPort<UsbBus>> = 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<T, R>(borrower: T) -> R
where
T: Fn(&mut SerialPort<UsbBus>) -> 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, R>(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<UsbBusAllocator<UsbBus>> = None;
static mut USB_BUS: Option<UsbDevice<UsbBus>> = None;
static mut USB_SERIAL: Option<SerialPort<UsbBus>> = 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);
Expand Down
6 changes: 3 additions & 3 deletions boards/feather_m0/examples/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use bsp::Pins;
use pac::{CorePeripherals, Peripherals};

use hal::{
adc::{Accumulation, Adc, Prescaler, Resolution},
adc::{Accumulation, Prescaler},
clock::GenericClockController,
};

Expand Down Expand Up @@ -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();
}
}
2 changes: 1 addition & 1 deletion boards/feather_m0/examples/async_adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
Loading
Loading