diff --git a/nrf52-code/boards/dongle/Cargo.toml b/nrf52-code/boards/dongle/Cargo.toml index 05694d37..460dc435 100644 --- a/nrf52-code/boards/dongle/Cargo.toml +++ b/nrf52-code/boards/dongle/Cargo.toml @@ -9,6 +9,7 @@ version = "0.0.0" cortex-m = {version = "0.7.6", features = ["critical-section-single-core"]} cortex-m-rt = "0.7.2" cortex-m-semihosting = "0.5.0" +critical-section = "1.1.2" defmt = "0.3.5" defmt-rtt = "0.4" embedded-hal = "0.2.7" diff --git a/nrf52-code/boards/dongle/src/lib.rs b/nrf52-code/boards/dongle/src/lib.rs index 480f3812..4325c66f 100644 --- a/nrf52-code/boards/dongle/src/lib.rs +++ b/nrf52-code/boards/dongle/src/lib.rs @@ -231,6 +231,51 @@ impl core::fmt::Write for &Ringbuffer { } } +/// The global type for sharing things with an interrupt handler +pub struct GlobalIrqState { + inner: critical_section::Mutex>>, +} + +impl GlobalIrqState { + /// Create a new, empty, object + pub const fn new() -> GlobalIrqState { + GlobalIrqState { + inner: critical_section::Mutex::new(core::cell::RefCell::new(None)), + } + } + + /// Load a value into the global + /// + /// Returns the old value, if any + pub fn load(&self, value: T) -> Option { + critical_section::with(|cs| self.inner.borrow(cs).replace(Some(value))) + } +} + +/// The local type for sharing things with an interrupt handler +pub struct LocalIrqState { + inner: Option, +} + +impl LocalIrqState { + /// Create a new, empty, object + pub const fn new() -> LocalIrqState { + LocalIrqState { inner: None } + } + + /// Grab a mutable reference to the contents. + /// + /// If the value is empty, the contents are taken from a mutex-locked global + /// variable. That global must have been initialised before calling this + /// function. If not, this function panics. + pub fn get_or_init_with(&mut self, global: &GlobalIrqState) -> &mut T { + let result = self.inner.get_or_insert_with(|| { + critical_section::with(|cs| global.inner.borrow(cs).replace(None).unwrap()) + }); + result + } +} + /// The ways that initialisation can fail #[derive(Debug, Copy, Clone, defmt::Format)] pub enum Error { diff --git a/nrf52-code/loopback-fw/Cargo.lock b/nrf52-code/loopback-fw/Cargo.lock index 23cdad2d..3f3c5ab1 100644 --- a/nrf52-code/loopback-fw/Cargo.lock +++ b/nrf52-code/loopback-fw/Cargo.lock @@ -160,6 +160,7 @@ dependencies = [ "cortex-m", "cortex-m-rt", "cortex-m-semihosting", + "critical-section", "defmt", "defmt-rtt", "embedded-hal", diff --git a/nrf52-code/loopback-fw/src/main.rs b/nrf52-code/loopback-fw/src/main.rs index cba73573..2d03315f 100644 --- a/nrf52-code/loopback-fw/src/main.rs +++ b/nrf52-code/loopback-fw/src/main.rs @@ -5,12 +5,10 @@ #![no_std] #![no_main] -use core::cell::RefCell; use core::fmt::Write; use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use cortex_m_rt::entry; -use critical_section::Mutex; use usb_device::class_prelude::UsbBusAllocator; use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}; use usbd_hid::hid_class::HIDClass; @@ -20,20 +18,20 @@ use dongle::peripheral::interrupt; use dongle::{ hal::usbd, ieee802154::{Channel, Packet}, - UsbBus, + GlobalIrqState, LocalIrqState, UsbBus, }; /// A buffer for holding bytes we want to send to the USB Serial port static RING_BUFFER: dongle::Ringbuffer = dongle::Ringbuffer::new(); /// The USB Device Driver (owned by the USBD interrupt). -static USB_DEVICE: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_DEVICE: GlobalIrqState> = GlobalIrqState::new(); /// The USB Serial Device Driver (owned by the USBD interrupt). -static USB_SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_SERIAL: GlobalIrqState> = GlobalIrqState::new(); /// The USB Human Interface Device Driver (owned by the USBD interrupt). -static USB_HID: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_HID: GlobalIrqState> = GlobalIrqState::new(); /// Track how many CRC successes we had receiving radio packets static RX_COUNT: AtomicU32 = AtomicU32::new(0); @@ -67,17 +65,14 @@ fn main() -> ! { board.usbd, board.clocks, ))); - *USB_BUS = Some(usb_bus); - - // This reference has static lifetime - let bus_ref = USB_BUS.as_ref().unwrap(); + USB_BUS.replace(usb_bus); // Grab a reference to the USB Bus allocator. We are promising to the // compiler not to take mutable access to this global variable whilst this // reference exists! - critical_section::with(|cs| { - *USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(bus_ref)); - }); + let bus_ref = USB_BUS.as_ref().unwrap(); + + USB_SERIAL.load(SerialPort::new(bus_ref)); let desc = &[ 0x06, 0x00, 0xFF, // Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 @@ -104,21 +99,17 @@ fn main() -> ! { // Preferred_State No_Null_Position Non_Volatile Bitfield 0xC0, // Item(Main ): End Collection, data=none ]; - let hid = HIDClass::new(bus_ref, desc, 100); - critical_section::with(|cs| { - *USB_HID.borrow(cs).borrow_mut() = Some(hid); - }); - - let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_PUZZLE); - let usb_dev = UsbDeviceBuilder::new(bus_ref, vid_pid) - .manufacturer("Ferrous Systems") - .product("Dongle Loopback") - .device_class(USB_CLASS_CDC) - .max_packet_size_0(64) // (makes control transfers 8x faster) - .build(); - critical_section::with(|cs| { - *USB_DEVICE.borrow(cs).borrow_mut() = Some(usb_dev); - }); + USB_HID.load(HIDClass::new(bus_ref, desc, 100)); + + let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_LOOPBACK); + USB_DEVICE.load( + UsbDeviceBuilder::new(bus_ref, vid_pid) + .manufacturer("Ferrous Systems") + .product("Dongle Loopback") + .device_class(USB_CLASS_CDC) + .max_packet_size_0(64) // (makes control transfers 8x faster) + .build(), + ); let mut current_ch_id = 20; board.radio.set_channel(dongle::ieee802154::Channel::_20); @@ -220,33 +211,15 @@ fn main() -> ! { /// USB UART. #[interrupt] fn USBD() { - static mut LOCAL_USB_DEVICE: Option> = None; - static mut LOCAL_USB_SERIAL: Option> = None; - static mut LOCAL_USB_HID: Option> = None; + static mut LOCAL_USB_DEVICE: LocalIrqState> = LocalIrqState::new(); + static mut LOCAL_USB_SERIAL: LocalIrqState> = LocalIrqState::new(); + static mut LOCAL_USB_HID: LocalIrqState> = LocalIrqState::new(); static mut IS_PENDING: Option = None; // Grab a reference to our local vars, moving the object out of the global as required... - - let usb_dev = LOCAL_USB_DEVICE.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_DEVICE.borrow(cs).replace(None).unwrap() - }) - }); - - let serial = LOCAL_USB_SERIAL.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_SERIAL.borrow(cs).replace(None).unwrap() - }) - }); - - let hid = LOCAL_USB_HID.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_HID.borrow(cs).replace(None).unwrap() - }) - }); + let usb_dev = LOCAL_USB_DEVICE.get_or_init_with(&USB_DEVICE); + let serial = LOCAL_USB_SERIAL.get_or_init_with(&USB_SERIAL); + let hid = LOCAL_USB_HID.get_or_init_with(&USB_HID); let mut buf = [0u8; 64]; diff --git a/nrf52-code/puzzle-fw/Cargo.lock b/nrf52-code/puzzle-fw/Cargo.lock index 5b593381..e73ca560 100644 --- a/nrf52-code/puzzle-fw/Cargo.lock +++ b/nrf52-code/puzzle-fw/Cargo.lock @@ -160,6 +160,7 @@ dependencies = [ "cortex-m", "cortex-m-rt", "cortex-m-semihosting", + "critical-section", "defmt", "defmt-rtt", "embedded-hal", diff --git a/nrf52-code/puzzle-fw/src/main.rs b/nrf52-code/puzzle-fw/src/main.rs index 06445c79..2527bfaa 100644 --- a/nrf52-code/puzzle-fw/src/main.rs +++ b/nrf52-code/puzzle-fw/src/main.rs @@ -5,12 +5,10 @@ #![no_std] #![no_main] -use core::cell::RefCell; use core::fmt::Write; use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use cortex_m_rt::entry; -use critical_section::Mutex; use usb_device::class_prelude::UsbBusAllocator; use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}; use usbd_hid::hid_class::HIDClass; @@ -20,7 +18,7 @@ use dongle::peripheral::interrupt; use dongle::{ hal::usbd, ieee802154::{Channel, Packet}, - UsbBus, + GlobalIrqState, LocalIrqState, UsbBus, }; /// The secret message, but encoded. @@ -38,13 +36,13 @@ static CIPHER_LETTERS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/CIPHER_ static RING_BUFFER: dongle::Ringbuffer = dongle::Ringbuffer::new(); /// The USB Device Driver (owned by the USBD interrupt). -static USB_DEVICE: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_DEVICE: GlobalIrqState> = GlobalIrqState::new(); /// The USB Serial Device Driver (owned by the USBD interrupt). -static USB_SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_SERIAL: GlobalIrqState> = GlobalIrqState::new(); /// The USB Human Interface Device Driver (owned by the USBD interrupt). -static USB_HID: Mutex>>> = Mutex::new(RefCell::new(None)); +static USB_HID: GlobalIrqState> = GlobalIrqState::new(); /// Track how many CRC successes we had receiving radio packets static RX_COUNT: AtomicU32 = AtomicU32::new(0); @@ -78,17 +76,14 @@ fn main() -> ! { board.usbd, board.clocks, ))); - *USB_BUS = Some(usb_bus); - - // This reference has static lifetime - let bus_ref = USB_BUS.as_ref().unwrap(); + USB_BUS.replace(usb_bus); // Grab a reference to the USB Bus allocator. We are promising to the // compiler not to take mutable access to this global variable whilst this // reference exists! - critical_section::with(|cs| { - *USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(bus_ref)); - }); + let bus_ref = USB_BUS.as_ref().unwrap(); + + USB_SERIAL.load(SerialPort::new(bus_ref)); let desc = &[ 0x06, 0x00, 0xFF, // Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 @@ -115,21 +110,17 @@ fn main() -> ! { // Preferred_State No_Null_Position Non_Volatile Bitfield 0xC0, // Item(Main ): End Collection, data=none ]; - let hid = HIDClass::new(bus_ref, desc, 100); - critical_section::with(|cs| { - *USB_HID.borrow(cs).borrow_mut() = Some(hid); - }); + USB_HID.load(HIDClass::new(bus_ref, desc, 100)); let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_PUZZLE); - let usb_dev = UsbDeviceBuilder::new(bus_ref, vid_pid) - .manufacturer("Ferrous Systems") - .product("Dongle Puzzle") - .device_class(USB_CLASS_CDC) - .max_packet_size_0(64) // (makes control transfers 8x faster) - .build(); - critical_section::with(|cs| { - *USB_DEVICE.borrow(cs).borrow_mut() = Some(usb_dev); - }); + USB_DEVICE.load( + UsbDeviceBuilder::new(bus_ref, vid_pid) + .manufacturer("Ferrous Systems") + .product("Dongle Puzzle") + .device_class(USB_CLASS_CDC) + .max_packet_size_0(64) // (makes control transfers 8x faster) + .build(), + ); let mut current_ch_id = 25; board.radio.set_channel(dongle::ieee802154::Channel::_25); @@ -173,7 +164,7 @@ fn main() -> ! { ); match handle_packet(&mut pkt, &dict) { Command::SendSecret => { - pkt.copy_from_slice(&ENCODED_MESSAGE); + pkt.copy_from_slice(ENCODED_MESSAGE); let _ = writeln!(&RING_BUFFER, "TX Secret"); board.leds.ld2_blue.on(); board.leds.ld2_green.off(); @@ -301,33 +292,15 @@ fn handle_packet(packet: &mut Packet, dict: &heapless::LinearMap) - /// USB UART. #[interrupt] fn USBD() { - static mut LOCAL_USB_DEVICE: Option> = None; - static mut LOCAL_USB_SERIAL: Option> = None; - static mut LOCAL_USB_HID: Option> = None; + static mut LOCAL_USB_DEVICE: LocalIrqState> = LocalIrqState::new(); + static mut LOCAL_USB_SERIAL: LocalIrqState> = LocalIrqState::new(); + static mut LOCAL_USB_HID: LocalIrqState> = LocalIrqState::new(); static mut IS_PENDING: Option = None; // Grab a reference to our local vars, moving the object out of the global as required... - - let usb_dev = LOCAL_USB_DEVICE.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_DEVICE.borrow(cs).replace(None).unwrap() - }) - }); - - let serial = LOCAL_USB_SERIAL.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_SERIAL.borrow(cs).replace(None).unwrap() - }) - }); - - let hid = LOCAL_USB_HID.get_or_insert_with(|| { - critical_section::with(|cs| { - // Move USB device here, leaving a None in its place - USB_HID.borrow(cs).replace(None).unwrap() - }) - }); + let usb_dev = LOCAL_USB_DEVICE.get_or_init_with(&USB_DEVICE); + let serial = LOCAL_USB_SERIAL.get_or_init_with(&USB_SERIAL); + let hid = LOCAL_USB_HID.get_or_init_with(&USB_HID); let mut buf = [0u8; 64];