diff --git a/boards/neo_trinkey/Cargo.toml b/boards/neo_trinkey/Cargo.toml index 743d465d4107..fbf454b22cf2 100644 --- a/boards/neo_trinkey/Cargo.toml +++ b/boards/neo_trinkey/Cargo.toml @@ -15,6 +15,8 @@ cortex-m-rt = { version = "0.7", optional = true } usb-device = { version = "0.2", optional = true } smart-leds = { version = "0.3.0", optional = true } ws2812-timer-delay = { version = "0.3.0", features = ["slow"], optional = true } +cargo-hf2 = "0.3.3" +fixed-slice-vec = "0.10.0" [dependencies.atsamd-hal] version = "0.14" diff --git a/boards/neo_trinkey/README.md b/boards/neo_trinkey/README.md index a91d7965b62e..95abd98cd9f4 100644 --- a/boards/neo_trinkey/README.md +++ b/boards/neo_trinkey/README.md @@ -1,3 +1,16 @@ +### How to run our first example: + +``` +cargo hf2 --release --example usb_morse --features usb,leds +``` +Connect to the device at /dev/cu.usbmodemTRINKEY_MORSE1 +```bash +$ screen /dev/cu.usbmodemTRINKEY_MORSE1 9600 +``` + + +``` + # Adafruit Neo Trinkey Board Support Crate This crate provides a type-safe API for working with the [Adafruit Neo Trinkey diff --git a/boards/neo_trinkey/examples/hex_color_converter.rs b/boards/neo_trinkey/examples/hex_color_converter.rs new file mode 100644 index 000000000000..42e0762ac9a9 --- /dev/null +++ b/boards/neo_trinkey/examples/hex_color_converter.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] + +pub fn hex_to_rgb(hex: &str) -> Result<(u8, u8, u8), &str> { + let hex = hex.trim_start_matches('#'); + + let result: Result<(u8, u8, u8), &str> = if hex.len() == 6 { + let r = u8::from_str_radix(&hex[0..2], 16).unwrap(); + let g = u8::from_str_radix(&hex[2..4], 16).unwrap(); + let b = u8::from_str_radix(&hex[4..6], 16).unwrap(); + Ok((r, g, b)) + } else if hex.len() == 3 { + let r = u8::from_str_radix(&hex[0..1], 16).unwrap(); + let g = u8::from_str_radix(&hex[1..2], 16).unwrap(); + let b = u8::from_str_radix(&hex[2..3], 16).unwrap(); + Ok((r * 16 + r, g * 16 + g, b * 16 + b)) + } else { + Err("Invalid color") + }; + + result +} + +#[cfg(test)] +mod tests { + use super::hex_to_rgb; + + #[test] + fn test_valid_hex_to_rgb() { + let success_cases: [(&str, (u8, u8, u8)); 7] = [ + ("#000000", (0, 0, 0)), + ("#ffffff", (255, 255, 255)), + ("#FF0000", (255, 0, 0)), + ("#00FF00", (0, 255, 0)), + ("#0000FF", (0, 0, 255)), + ("#F0F0F0", (240, 240, 240)), + ("#abcdef", (171, 205, 239)), + ]; + + for case in success_cases.iter() { + let (hex, expected) = case; + let result = hex_to_rgb(hex); + + assert_eq!(result.ok().unwrap(), *expected, "hex: {}", hex); + } + } + + #[test] + fn test_invalid_hex_to_rgb() { + let invalid_result = hex_to_rgb("invalid"); + assert_eq!(invalid_result.err().unwrap(), "Invalid color"); + } +} \ No newline at end of file diff --git a/boards/neo_trinkey/examples/morse_queues.rs b/boards/neo_trinkey/examples/morse_queues.rs new file mode 100644 index 000000000000..a0a0bb60ed51 --- /dev/null +++ b/boards/neo_trinkey/examples/morse_queues.rs @@ -0,0 +1,706 @@ +// const INTERVAL: u16 = 100u16; // Controls the speed of morse code generation + +pub mod morse_queues { + const CHAR_QUEUE_LENTGH: usize = 1024; + const STATE_QUEUE_LENGTH: usize = 32; + + pub struct CharQueue { + queue: [char; CHAR_QUEUE_LENTGH], + length: usize, + write_position: usize, + read_position: usize, + } + + pub struct PinControlQueue { + queue: [PinControlDescriptor; STATE_QUEUE_LENGTH], + length: usize, + write_position: usize, + read_position: usize, + } + + static mut CHAR_QUEUE: CharQueue = CharQueue { + queue: ['\r'; CHAR_QUEUE_LENTGH], + length: 0, + write_position: 0, + read_position: 0, + }; + + static mut PIN_CONTROL_QUEUE: PinControlQueue = PinControlQueue { + queue: [ + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + ], + length: 0, + write_position: 0, + read_position: 0, + }; + + pub struct PinControlDescriptor { + pin_state: bool, + duration: u8, + } + + pub fn push_dot() { + let dot = PinControlDescriptor { + pin_state: true, + duration: 1, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dot); + push_state(interval); + } + + pub fn push_dash() { + let dash = PinControlDescriptor { + pin_state: true, + duration: 3, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dash); + push_state(interval); + } + + pub fn push_space() { + let space = PinControlDescriptor { + pin_state: false, + duration: 7, + }; + push_state(space); + } + + pub fn push_letter_interval() { + let space = PinControlDescriptor { + pin_state: false, + duration: 3, + }; + push_state(space); + } + + static mut COUNT_DOWN: u8 = 0; + static mut CURRENT_PIN_STATE: bool = false; + + pub fn get_next_state() -> bool { + let mut pin_state: bool; + unsafe { + if COUNT_DOWN <= 0 { + if PIN_CONTROL_QUEUE.length == 0 { + // Only push a new character into our que if we've drained it fully + emit_morse_letter(pop_char()); + } + let pin_state_descriptor = pop_state(); + COUNT_DOWN = pin_state_descriptor.duration; + CURRENT_PIN_STATE = pin_state_descriptor.pin_state; + } + COUNT_DOWN -= 1; + return CURRENT_PIN_STATE; + } + } + + pub fn push_char(letter: char) { + unsafe { + let index = CHAR_QUEUE.write_position as usize; + let queue = &mut CHAR_QUEUE; + queue.queue[index] = letter; + CHAR_QUEUE.write_position = (CHAR_QUEUE.write_position + 1) % CHAR_QUEUE_LENTGH; + queue.length += 1; + } + } + + pub fn pop_char() -> char { + unsafe { + if CHAR_QUEUE.length == 0 { + return '\r'; // Use CR to indicate empty queue + } + let return_value = CHAR_QUEUE.queue[CHAR_QUEUE.read_position]; + CHAR_QUEUE.read_position = (CHAR_QUEUE.read_position + 1) % CHAR_QUEUE_LENTGH; + CHAR_QUEUE.length -= 1; + return return_value; + } + } + + pub fn push_state(state: PinControlDescriptor) { + unsafe { + let index = PIN_CONTROL_QUEUE.write_position as usize; + let queue = &mut PIN_CONTROL_QUEUE; + queue.queue[index] = state; + PIN_CONTROL_QUEUE.write_position = + (PIN_CONTROL_QUEUE.write_position + 1) % STATE_QUEUE_LENGTH; + queue.length += 1; + } + } + + pub fn pop_state() -> PinControlDescriptor { + unsafe { + if PIN_CONTROL_QUEUE.length == 0 { + return PinControlDescriptor { + pin_state: false, + duration: 1, + }; + } + let return_value = &PIN_CONTROL_QUEUE.queue[PIN_CONTROL_QUEUE.read_position]; + PIN_CONTROL_QUEUE.length -= 1; + PIN_CONTROL_QUEUE.read_position = + (PIN_CONTROL_QUEUE.read_position + 1) % STATE_QUEUE_LENGTH; + return PinControlDescriptor { + pin_state: return_value.pin_state, + duration: return_value.duration, + }; + } + } + + pub fn emit_morse_letter(letter: char) { + let downcased_letter = letter.to_ascii_lowercase(); // Add support for Latin 1 later. + match downcased_letter { + 'a' => { + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'b' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'c' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'd' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'e' => { + emit_morse_dot(); + push_letter_interval(); + } + 'f' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'g' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'h' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'i' => { + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'j' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'k' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'l' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'm' => { + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'n' => { + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'o' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'p' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'q' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'r' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 's' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 't' => { + emit_morse_dash(); + push_letter_interval(); + } + 'u' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'v' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'w' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'x' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'y' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'z' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '0' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '1' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '2' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '3' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '4' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '5' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '6' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '7' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '8' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '9' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ' ' => { + push_space(); + } + ',' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '.' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '?' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '\'' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '!' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '/' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '(' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ')' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '&' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ':' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ';' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '=' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '+' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '-' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '_' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '"' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '$' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '@' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '\r' => { + // We use CR to indicate the quueue is empty, so we make it phs only one blank cycle. + push_letter_interval(); + } + _ => { + emit_morse_space(); + } + } + } + + pub fn emit_morse_dot() { + push_dot(); + } + pub fn emit_morse_dash() { + push_dash(); + } + pub fn emit_morse_space() { + push_space(); + } +} diff --git a/boards/neo_trinkey/examples/usb_colors.rs b/boards/neo_trinkey/examples/usb_colors.rs new file mode 100644 index 000000000000..1e91548cfc30 --- /dev/null +++ b/boards/neo_trinkey/examples/usb_colors.rs @@ -0,0 +1,873 @@ +#![no_std] +#![no_main] +use bsp::pac::dsu::length; +use panic_halt as _; + +use core::mem::MaybeUninit; +use fixed_slice_vec::FixedSliceVec; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use neo_trinkey as bsp; + +use bsp::entry; +use bsp::hal; +use bsp::pac; + +use hal::clock::GenericClockController; +use hal::usb::UsbBus; + +// Added +use hal::delay::Delay; +// use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::timer::TimerCounter; + +use smart_leds::{hsv::RGB8, SmartLedsWrite}; +use ws2812_timer_delay::Ws2812; +// End Added +use pac::{interrupt, CorePeripherals, Peripherals}; + +mod hex_color_converter; + +const INTERVAL: u16 = 100u16; // Controls the speed of morse code generation +const CHAR_QUEUE_LENTGH: usize = 1024; +const STATE_QUEUE_LENGTH: usize = 32; + +struct CharQueue { + queue: [char; CHAR_QUEUE_LENTGH], + length: usize, + write_position: usize, + read_position: usize, +} + +struct PinControlQueue { + queue: [PinControlDescriptor; STATE_QUEUE_LENGTH], + length: usize, + write_position: usize, + read_position: usize, +} + +static mut CHAR_QUEUE: CharQueue = CharQueue { + queue: ['\r'; CHAR_QUEUE_LENTGH], + length: 0, + write_position: 0, + read_position: 0, +}; + +static mut PIN_CONTROL_QUEUE: PinControlQueue = PinControlQueue { + queue: [ + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + ], + length: 0, + write_position: 0, + read_position: 0, +}; + +struct PinControlDescriptor { + pin_state: bool, + duration: u8, +} + +fn push_dot() { + let dot = PinControlDescriptor { + pin_state: true, + duration: 1, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dot); + push_state(interval); +} + +fn push_dash() { + let dash = PinControlDescriptor { + pin_state: true, + duration: 3, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dash); + push_state(interval); +} + +fn push_space() { + let space = PinControlDescriptor { + pin_state: false, + duration: 7, + }; + push_state(space); +} + +fn push_letter_interval() { + let space = PinControlDescriptor { + pin_state: false, + duration: 3, + }; + push_state(space); +} + +static mut COUNT_DOWN: u8 = 0; +static mut CURRENT_PIN_STATE: bool = false; + +fn get_next_state() -> bool { + let mut pin_state: bool; + unsafe { + if COUNT_DOWN <= 0 { + if PIN_CONTROL_QUEUE.length == 0 { + // Only push a new character into our que if we've drained it fully + emit_morse_letter(pop_char()); + } + let pin_state_descriptor = pop_state(); + COUNT_DOWN = pin_state_descriptor.duration; + CURRENT_PIN_STATE = pin_state_descriptor.pin_state; + } + COUNT_DOWN -= 1; + return CURRENT_PIN_STATE; + } +} + +fn push_char(letter: char) { + unsafe { + let index = CHAR_QUEUE.write_position as usize; + let queue = &mut CHAR_QUEUE; + queue.queue[index] = letter; + CHAR_QUEUE.write_position = (CHAR_QUEUE.write_position + 1) % CHAR_QUEUE_LENTGH; + queue.length += 1; + } +} + +fn pop_char() -> char { + unsafe { + if CHAR_QUEUE.length == 0 { + return '\r'; // Use CR to indicate empty queue + } + let return_value = CHAR_QUEUE.queue[CHAR_QUEUE.read_position]; + CHAR_QUEUE.read_position = (CHAR_QUEUE.read_position + 1) % CHAR_QUEUE_LENTGH; + CHAR_QUEUE.length -= 1; + return return_value; + } +} + +fn push_state(state: PinControlDescriptor) { + unsafe { + let index = PIN_CONTROL_QUEUE.write_position as usize; + let queue = &mut PIN_CONTROL_QUEUE; + queue.queue[index] = state; + PIN_CONTROL_QUEUE.write_position = + (PIN_CONTROL_QUEUE.write_position + 1) % STATE_QUEUE_LENGTH; + queue.length += 1; + } +} + +fn pop_state() -> PinControlDescriptor { + unsafe { + if PIN_CONTROL_QUEUE.length == 0 { + return PinControlDescriptor { + pin_state: false, + duration: 1, + }; + } + let return_value = &PIN_CONTROL_QUEUE.queue[PIN_CONTROL_QUEUE.read_position]; + PIN_CONTROL_QUEUE.length -= 1; + PIN_CONTROL_QUEUE.read_position = + (PIN_CONTROL_QUEUE.read_position + 1) % STATE_QUEUE_LENGTH; + return PinControlDescriptor { + pin_state: return_value.pin_state, + duration: return_value.duration, + }; + } +} + +#[entry] +fn main() -> ! { + let result = hex_color_converter::hex_to_rgb("123"); + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Agilistas!") + .product("Serial Port Morse Emitter") + .serial_number("TRINKY_MORSE") + .device_class(USB_CLASS_CDC) + .build(), + ); + // USB_BUS.event_queue = Some(&mut USB_BUS_EVENTS); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); + timer.start(3.mhz()); + let neo_pixel = pins.neo_pixel.into_push_pull_output(); + let mut ws2812 = Ws2812::new(timer, neo_pixel); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + const NUM_LEDS: usize = 4; + let off = [RGB8::default(); NUM_LEDS]; + let on = [ + RGB8::new(5, 5, 0), + RGB8::new(0, 5, 5), + RGB8::new(5, 0, 5), + RGB8::new(2, 2, 2), + ]; + + loop { + // let letter = pop(); + // emit_morse_letter(letter); + let state = get_next_state(); + if state { + // turn on + ws2812.write(on.iter().cloned()).unwrap(); + } else { + // turn off + ws2812.write(off.iter().cloned()).unwrap(); + } + + // ws2812.write(off.iter().cloned()).unwrap(); + delay.delay_ms(INTERVAL); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + USB_BUS.as_mut().map(|usb_dev| { + USB_SERIAL.as_mut().map(|serial| { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write("Received: ".as_bytes()).ok(); + serial.write(&[c.clone()]).ok(); + // serial.write("\tCharacter queue size: ".as_bytes()).ok(); // Hmmm. How do I do this in embedded Rust? + // let queue_size = i32::from(CHAR_QUEUE.length); + // serial.write(queue_size.to_str().as_bytes()).ok(); + serial.write("\r\n".as_bytes()).ok(); + // emit_morse_letter('e'); + + let letter = char::from_u32([c.clone()][0] as u32).unwrap(); + push_char(letter); + // emit_morse_letter(letter); // TODO: Move this to next state)_ + } + }; + }); + }); + }; +} + +fn emit_morse_letter(letter: char) { + let downcased_letter = letter.to_ascii_lowercase(); // Add support for Latin 1 later. + match downcased_letter { + 'a' => { + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'b' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'c' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'd' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'e' => { + emit_morse_dot(); + push_letter_interval(); + } + 'f' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'g' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'h' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'i' => { + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'j' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'k' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'l' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'm' => { + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'n' => { + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'o' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'p' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'q' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'r' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 's' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 't' => { + emit_morse_dash(); + push_letter_interval(); + } + 'u' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'v' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'w' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'x' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'y' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'z' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '0' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '1' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '2' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '3' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '4' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '5' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '6' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '7' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '8' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '9' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ' ' => { + push_space(); + } + ',' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '.' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '?' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '\'' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '!' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '/' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '(' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ')' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '&' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ':' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ';' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '=' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '+' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '-' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '_' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '"' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '$' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '@' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '\r' => { + // We use CR to indicate the quueue is empty, so we make it phs only one blank cycle. + push_letter_interval(); + } + _ => { + emit_morse_space(); + } + } +} + +fn emit_morse_dot() { + push_dot(); +} +fn emit_morse_dash() { + push_dash(); +} +fn emit_morse_space() { + push_space(); +} + +#[interrupt] +fn USB() { + // Note: USB is the name of the interrupt, you can not attach the #[interrupt] + // tag to poll_usb. Although you could add the contents of poll_usb into + // this function, separating them allows you to add more functions to run on + // the USB interrupt in the future. + poll_usb(); +} + +fn print_serial_header() { + unsafe { + let serial = USB_SERIAL.as_mut().unwrap(); + serial + .write("Connected to Neo Trinky Morse Echo.\r\n: ".as_bytes()) + .ok(); + serial + .write( + "Type letters and the LED will emit their morse code equivalents.\r\n: ".as_bytes(), + ) + .ok(); + } +} \ No newline at end of file diff --git a/boards/neo_trinkey/examples/usb_morse-xiao.rs b/boards/neo_trinkey/examples/usb_morse-xiao.rs new file mode 100644 index 000000000000..96a8ba60adb3 --- /dev/null +++ b/boards/neo_trinkey/examples/usb_morse-xiao.rs @@ -0,0 +1,99 @@ +#![no_std] +#![no_main] + +extern crate panic_halt; + +use cortex_m::{asm::delay as asm_delay, peripheral::NVIC}; +use hal::{clock::GenericClockController, prelude::*, usb::UsbBus}; +use pac::{interrupt, CorePeripherals, Peripherals}; +use usb_device::{bus::UsbBusAllocator, prelude::*}; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use bsp::{entry, hal, pac, Led0, Led1}; +use neo_trinkey as bsp; + +struct UsbSerial { + usb_bus: UsbBusAllocator, + serial: SerialPort<'static, UsbBus>, + led_data: Led1, +} + +struct Environment { + usb_serial: Option, + led0: Led0, + peripherals: Peripherals, + core: CorePeripherals, + clocks: GenericClockController, + pins: bsp::Pins, + bus_allocator: UsbBusAllocator, +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; +static mut LED_DATA: Option = None; + +fn init() -> Environment { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + let usb_serial = unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0xdead, 0xbeef)) + .manufacturer("Agilistas!") + .product("xiao_usb_morse") + .serial_number("1") + .device_class(USB_CLASS_CDC) + .build(), + ); + LED_DATA = Some(pins.led1.into_mode()); + UsbSerial { + usb_bus: USB_ALLOCATOR.as_ref().unwrap(), + serial: USB_SERIAL.as_ref().unwrap(), + led_data: LED_DATA.as_ref().unwrap(), + } + }; + + Environment { + usb_serial: Some(usb_serial), + led0: pins.led0.into_push_pull_output(), + peripherals, + core, + clocks, + pins, + bus_allocator, + } +} + +#[entry] +fn main() -> ! { + let mut environment: Environment = init(); + let mut led0: Led0 = environment.pins.led0.into_push_pull_output(); + let mut delay = hal::delay::Delay::new(environment.core.SYST, &mut environment.clocks); + + loop { + delay.delay_ms(200u8); + led0.toggle().unwrap(); + delay.delay_ms(600u16); + led0.toggle().unwrap(); + } +} diff --git a/boards/neo_trinkey/examples/usb_morse.rs b/boards/neo_trinkey/examples/usb_morse.rs new file mode 100644 index 000000000000..c5f8aaf54590 --- /dev/null +++ b/boards/neo_trinkey/examples/usb_morse.rs @@ -0,0 +1,870 @@ +#![no_std] +#![no_main] +use bsp::pac::dsu::length; +use panic_halt as _; + +use core::mem::MaybeUninit; +use fixed_slice_vec::FixedSliceVec; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use neo_trinkey as bsp; + +use bsp::entry; +use bsp::hal; +use bsp::pac; + +use hal::clock::GenericClockController; +use hal::usb::UsbBus; + +// Added +use hal::delay::Delay; +// use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::timer::TimerCounter; + +use smart_leds::{hsv::RGB8, SmartLedsWrite}; +use ws2812_timer_delay::Ws2812; +// End Added +use pac::{interrupt, CorePeripherals, Peripherals}; + +const INTERVAL: u16 = 100u16; // Controls the speed of morse code generation +const CHAR_QUEUE_LENTGH: usize = 1024; +const STATE_QUEUE_LENGTH: usize = 32; + +struct CharQueue { + queue: [char; CHAR_QUEUE_LENTGH], + length: usize, + write_position: usize, + read_position: usize, +} + +struct PinControlQueue { + queue: [PinControlDescriptor; STATE_QUEUE_LENGTH], + length: usize, + write_position: usize, + read_position: usize, +} + +static mut CHAR_QUEUE: CharQueue = CharQueue { + queue: ['\r'; CHAR_QUEUE_LENTGH], + length: 0, + write_position: 0, + read_position: 0, +}; + +static mut PIN_CONTROL_QUEUE: PinControlQueue = PinControlQueue { + queue: [ + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + PinControlDescriptor { + pin_state: false, + duration: 0, + }, + ], + length: 0, + write_position: 0, + read_position: 0, +}; + +struct PinControlDescriptor { + pin_state: bool, + duration: u8, +} + +fn push_dot() { + let dot = PinControlDescriptor { + pin_state: true, + duration: 1, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dot); + push_state(interval); +} + +fn push_dash() { + let dash = PinControlDescriptor { + pin_state: true, + duration: 3, + }; + let interval = PinControlDescriptor { + pin_state: false, + duration: 1, + }; + push_state(dash); + push_state(interval); +} + +fn push_space() { + let space = PinControlDescriptor { + pin_state: false, + duration: 7, + }; + push_state(space); +} + +fn push_letter_interval() { + let space = PinControlDescriptor { + pin_state: false, + duration: 3, + }; + push_state(space); +} + +static mut COUNT_DOWN: u8 = 0; +static mut CURRENT_PIN_STATE: bool = false; + +fn get_next_state() -> bool { + let mut pin_state: bool; + unsafe { + if COUNT_DOWN <= 0 { + if PIN_CONTROL_QUEUE.length == 0 { + // Only push a new character into our que if we've drained it fully + emit_morse_letter(pop_char()); + } + let pin_state_descriptor = pop_state(); + COUNT_DOWN = pin_state_descriptor.duration; + CURRENT_PIN_STATE = pin_state_descriptor.pin_state; + } + COUNT_DOWN -= 1; + return CURRENT_PIN_STATE; + } +} + +fn push_char(letter: char) { + unsafe { + let index = CHAR_QUEUE.write_position as usize; + let queue = &mut CHAR_QUEUE; + queue.queue[index] = letter; + CHAR_QUEUE.write_position = (CHAR_QUEUE.write_position + 1) % CHAR_QUEUE_LENTGH; + queue.length += 1; + } +} + +fn pop_char() -> char { + unsafe { + if CHAR_QUEUE.length == 0 { + return '\r'; // Use CR to indicate empty queue + } + let return_value = CHAR_QUEUE.queue[CHAR_QUEUE.read_position]; + CHAR_QUEUE.read_position = (CHAR_QUEUE.read_position + 1) % CHAR_QUEUE_LENTGH; + CHAR_QUEUE.length -= 1; + return return_value; + } +} + +fn push_state(state: PinControlDescriptor) { + unsafe { + let index = PIN_CONTROL_QUEUE.write_position as usize; + let queue = &mut PIN_CONTROL_QUEUE; + queue.queue[index] = state; + PIN_CONTROL_QUEUE.write_position = + (PIN_CONTROL_QUEUE.write_position + 1) % STATE_QUEUE_LENGTH; + queue.length += 1; + } +} + +fn pop_state() -> PinControlDescriptor { + unsafe { + if PIN_CONTROL_QUEUE.length == 0 { + return PinControlDescriptor { + pin_state: false, + duration: 1, + }; + } + let return_value = &PIN_CONTROL_QUEUE.queue[PIN_CONTROL_QUEUE.read_position]; + PIN_CONTROL_QUEUE.length -= 1; + PIN_CONTROL_QUEUE.read_position = + (PIN_CONTROL_QUEUE.read_position + 1) % STATE_QUEUE_LENGTH; + return PinControlDescriptor { + pin_state: return_value.pin_state, + duration: return_value.duration, + }; + } +} + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Agilistas!") + .product("Serial Port Morse Emitter") + .serial_number("TRINKY_MORSE") + .device_class(USB_CLASS_CDC) + .build(), + ); + // USB_BUS.event_queue = Some(&mut USB_BUS_EVENTS); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); + timer.start(3.mhz()); + let neo_pixel = pins.neo_pixel.into_push_pull_output(); + let mut ws2812 = Ws2812::new(timer, neo_pixel); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + const NUM_LEDS: usize = 4; + let off = [RGB8::default(); NUM_LEDS]; + let on = [ + RGB8::new(5, 5, 0), + RGB8::new(0, 5, 5), + RGB8::new(5, 0, 5), + RGB8::new(2, 2, 2), + ]; + + loop { + // let letter = pop(); + // emit_morse_letter(letter); + let state = get_next_state(); + if state { + // turn on + ws2812.write(on.iter().cloned()).unwrap(); + } else { + // turn off + ws2812.write(off.iter().cloned()).unwrap(); + } + + // ws2812.write(off.iter().cloned()).unwrap(); + delay.delay_ms(INTERVAL); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + USB_BUS.as_mut().map(|usb_dev| { + USB_SERIAL.as_mut().map(|serial| { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write("Received: ".as_bytes()).ok(); + serial.write(&[c.clone()]).ok(); + // serial.write("\tCharacter queue size: ".as_bytes()).ok(); // Hmmm. How do I do this in embedded Rust? + // let queue_size = i32::from(CHAR_QUEUE.length); + // serial.write(queue_size.to_str().as_bytes()).ok(); + serial.write("\r\n".as_bytes()).ok(); + // emit_morse_letter('e'); + + let letter = char::from_u32([c.clone()][0] as u32).unwrap(); + push_char(letter); + // emit_morse_letter(letter); // TODO: Move this to next state)_ + } + }; + }); + }); + }; +} + +fn emit_morse_letter(letter: char) { + let downcased_letter = letter.to_ascii_lowercase(); // Add support for Latin 1 later. + match downcased_letter { + 'a' => { + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'b' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'c' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'd' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'e' => { + emit_morse_dot(); + push_letter_interval(); + } + 'f' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'g' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'h' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'i' => { + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'j' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'k' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'l' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 'm' => { + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'n' => { + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'o' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'p' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 'q' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'r' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + 's' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + 't' => { + emit_morse_dash(); + push_letter_interval(); + } + 'u' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'v' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'w' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'x' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + 'y' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + 'z' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '0' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '1' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '2' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '3' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '4' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '5' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '6' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '7' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '8' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '9' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ' ' => { + push_space(); + } + ',' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '.' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '?' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + '\'' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '!' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + push_letter_interval(); + } + '/' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '(' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + ')' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '&' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ':' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + push_letter_interval(); + } + ';' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '=' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '+' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '-' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '_' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + push_letter_interval(); + } + '"' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '$' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '@' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + push_letter_interval(); + } + '\r' => { + // We use CR to indicate the quueue is empty, so we make it phs only one blank cycle. + push_letter_interval(); + } + _ => { + emit_morse_space(); + } + } +} + +fn emit_morse_dot() { + push_dot(); +} +fn emit_morse_dash() { + push_dash(); +} +fn emit_morse_space() { + push_space(); +} + +#[interrupt] +fn USB() { + // Note: USB is the name of the interrupt, you can not attach the #[interrupt] + // tag to poll_usb. Although you could add the contents of poll_usb into + // this function, separating them allows you to add more functions to run on + // the USB interrupt in the future. + poll_usb(); +} + +fn print_serial_header() { + unsafe { + let serial = USB_SERIAL.as_mut().unwrap(); + serial + .write("Connected to Neo Trinky Morse Echo.\r\n: ".as_bytes()) + .ok(); + serial + .write( + "Type letters and the LED will emit their morse code equivalents.\r\n: ".as_bytes(), + ) + .ok(); + } +} diff --git a/boards/neo_trinkey/examples/usb_morse_abstracted.rs b/boards/neo_trinkey/examples/usb_morse_abstracted.rs new file mode 100644 index 000000000000..64f32a4f31d1 --- /dev/null +++ b/boards/neo_trinkey/examples/usb_morse_abstracted.rs @@ -0,0 +1,358 @@ +#![no_std] +#![no_main] +use panic_halt as _; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use neo_trinkey as bsp; + +use bsp::entry; +use bsp::hal; +use bsp::pac; + +use hal::clock::GenericClockController; +use hal::usb::UsbBus; + +// Added +use hal::delay::Delay; +// use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::timer::TimerCounter; + +use smart_leds::{hsv::RGB8, SmartLedsWrite}; +use ws2812_timer_delay::Ws2812; +// End Added +use pac::{interrupt, CorePeripherals, Peripherals}; + +struct Environment { + // usb_serial: Option, + // led0: Led0, + peripherals: Peripherals, + core: CorePeripherals, + // clocks: GenericClockController, + // pins: bsp::Pins, + // bus_allocator: UsbBusAllocator, +} + +#[entry] +fn main() -> ! { + let mut env = init(); + legacy(env) +} + +fn init() -> Environment { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + + + // Environment { + // usb_serial: Some(usb_serial), + // led0: pins.led0.into_push_pull_output(), + // peripherals, + // core, + // clocks, + // pins, + // bus_allocator, + // } + Environment { + peripherals, + core, + } +} + +fn legacy(env: Environment) -> ! { + let mut peripherals = env.peripherals; + let mut core = env.core; + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Agilistas!") + .product("Serial port") + .serial_number("TRINKEY_MORSE") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); + timer.start(3.mhz()); + let neo_pixel = pins.neo_pixel.into_push_pull_output(); + let mut ws2812 = Ws2812::new(timer, neo_pixel); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + const NUM_LEDS: usize = 4; + let off = [RGB8::default(); NUM_LEDS]; + let on = [ + RGB8::new(5, 5, 0), + RGB8::new(0, 5, 5), + RGB8::new(5, 0, 5), + RGB8::new(2, 2, 2), + ]; + + // runTheLoop(); + loop { + ws2812.write(off.iter().cloned()).unwrap(); + delay.delay_ms(500u16); + ws2812.write(on.iter().cloned()).unwrap(); + delay.delay_ms(500u16); + + cycle_delay(15 * 1024 * 1024); + } + +} + +fn runTheLoop(env: Environment) -> ! { + loop { + // ws2812.write(off.iter().cloned()).unwrap(); + // delay.delay_ms(500u16); + // ws2812.write(on.iter().cloned()).unwrap(); + // delay.delay_ms(500u16); + + // cycle_delay(15 * 1024 * 1024); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + USB_BUS.as_mut().map(|usb_dev| { + USB_SERIAL.as_mut().map(|serial| { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write("Received: ".as_bytes()).ok(); + serial.write(&[c.clone()]).ok(); + serial.write("\r\n".as_bytes()).ok(); + emit_morse_letter('e'); + // emit_morse_letter(&[c.clone()].as_char()); + } + }; + }); + }); + }; +} + +fn print_to_serial(message: &str) { + unsafe { + USB_BUS.as_mut().map(|usb_dev| { + USB_SERIAL.as_mut().map(|serial| { + serial.write(message.as_bytes()).ok(); + }); + }); + }; +} + +fn emit_morse_letter(letter: char) { + let downcased_letter = letter.to_ascii_lowercase(); // Add support for Latin 1 later. + match downcased_letter { + 'a' => { + emit_morse_dot(); + emit_morse_dash(); + } + 'b' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + } + 'c' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + } + 'd' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + } + 'e' => emit_morse_dot(), + 'f' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + } + 'g' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + } + 'h' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + } + 'i' => { + emit_morse_dot(); + emit_morse_dot(); + } + 'j' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + } + 'k' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + } + 'l' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + } + 'm' => { + emit_morse_dash(); + emit_morse_dash(); + } + 'n' => { + emit_morse_dash(); + emit_morse_dash(); + } + 'o' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dash(); + } + 'p' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + } + 'q' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + } + 'r' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dot(); + } + 's' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + } + 't' => emit_morse_dash(), + 'u' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + } + 'v' => { + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + } + 'w' => { + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + } + 'x' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + emit_morse_dash(); + } + 'y' => { + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dash(); + emit_morse_dash(); + } + 'z' => { + emit_morse_dash(); + emit_morse_dash(); + emit_morse_dot(); + emit_morse_dot(); + } + _ => { + emit_morse_space(); + } + } +} + +const INTERVAL: u16 = 500u16; + +fn emit_morse_dot() { + print_to_serial("."); + // let neo_pixel = pins.neo_pixel.into_push_pull_output(); + // let mut ws2812 = Ws2812::new(timer, neo_pixel); + // ws2812.write(on.iter().cloned()).unwrap(); + // delay.delay_ms(INTERVAL); + // ws2812.write(off.iter().cloned()).unwrap(); + // delay.delay_ms(INTERVAL); +} +fn emit_morse_dash() { + print_to_serial("_"); + // let neo_pixel = pins.neo_pixel.into_push_pull_output(); + // let mut ws2812 = Ws2812::new(timer, neo_pixel); + // ws2812.write(on.iter().cloned()).unwrap(); + // delay.delay_ms(3 * INTERVAL); + // ws2812.write(off.iter().cloned()).unwrap(); + // delay.delay_ms(INTERVAL); +} +fn emit_morse_space() { + // let neo_pixel = pins.neo_pixel.into_push_pull_output(); + // let mut ws2812 = Ws2812::new(timer, neo_pixel); + // ws2812.write(off.iter().cloned()).unwrap(); + // delay.delay_ms(7 * INTERVAL); +} + +#[interrupt] +fn USB() { + // Note: USB is the name of the interrupt, you can not attach the #[interrupt] + // tag to poll_usb. Although you could add the contents of poll_usb into + // this function, separating them allows you to add more functions to run on + // the USB interrupt in the future. + poll_usb(); +} diff --git a/boards/neo_trinkey/examples/usb_morse_with_modules.rs b/boards/neo_trinkey/examples/usb_morse_with_modules.rs new file mode 100644 index 000000000000..1fc22445f6e3 --- /dev/null +++ b/boards/neo_trinkey/examples/usb_morse_with_modules.rs @@ -0,0 +1,874 @@ +#![no_std] +#![no_main] +use bsp::pac::dsu::length; +use panic_halt as _; + +use core::mem::MaybeUninit; +use fixed_slice_vec::FixedSliceVec; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use neo_trinkey as bsp; + +use bsp::entry; +use bsp::hal; +use bsp::pac; + +use hal::clock::GenericClockController; +use hal::usb::UsbBus; + +// Added +use hal::delay::Delay; +// use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::timer::TimerCounter; + +use smart_leds::{hsv::RGB8, SmartLedsWrite}; +use ws2812_timer_delay::Ws2812; +// End Added +use pac::{interrupt, CorePeripherals, Peripherals}; + +pub mod morse_queues; +use crate::morse_queues::morse_queues::get_next_state; +use crate::morse_queues::morse_queues::push_char; + +const INTERVAL: u16 = 100u16; // Controls the speed of morse code generation + // const CHAR_QUEUE_LENTGH: usize = 1024; + // const STATE_QUEUE_LENGTH: usize = 32; + +// struct CharQueue { +// queue: [char; CHAR_QUEUE_LENTGH], +// length: usize, +// write_position: usize, +// read_position: usize, +// } + +// struct PinControlQueue { +// queue: [PinControlDescriptor; STATE_QUEUE_LENGTH], +// length: usize, +// write_position: usize, +// read_position: usize, +// } + +// static mut CHAR_QUEUE: CharQueue = CharQueue { +// queue: ['\r'; CHAR_QUEUE_LENTGH], +// length: 0, +// write_position: 0, +// read_position: 0, +// }; + +// static mut PIN_CONTROL_QUEUE: PinControlQueue = PinControlQueue { +// queue: [ +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// PinControlDescriptor { +// pin_state: false, +// duration: 0, +// }, +// ], +// length: 0, +// write_position: 0, +// read_position: 0, +// }; + +// struct PinControlDescriptor { +// pin_state: bool, +// duration: u8, +// } + +// fn push_dot() { +// let dot = PinControlDescriptor { +// pin_state: true, +// duration: 1, +// }; +// let interval = PinControlDescriptor { +// pin_state: false, +// duration: 1, +// }; +// push_state(dot); +// push_state(interval); +// } + +// fn push_dash() { +// let dash = PinControlDescriptor { +// pin_state: true, +// duration: 3, +// }; +// let interval = PinControlDescriptor { +// pin_state: false, +// duration: 1, +// }; +// push_state(dash); +// push_state(interval); +// } + +// fn push_space() { +// let space = PinControlDescriptor { +// pin_state: false, +// duration: 7, +// }; +// push_state(space); +// } + +// fn push_letter_interval() { +// let space = PinControlDescriptor { +// pin_state: false, +// duration: 3, +// }; +// push_state(space); +// } + +// static mut COUNT_DOWN: u8 = 0; +// static mut CURRENT_PIN_STATE: bool = false; + +// fn get_next_state() -> bool { +// let mut pin_state: bool; +// unsafe { +// if COUNT_DOWN <= 0 { +// if PIN_CONTROL_QUEUE.length == 0 { +// // Only push a new character into our que if we've drained it fully +// emit_morse_letter(pop_char()); +// } +// let pin_state_descriptor = pop_state(); +// COUNT_DOWN = pin_state_descriptor.duration; +// CURRENT_PIN_STATE = pin_state_descriptor.pin_state; +// } +// COUNT_DOWN -= 1; +// return CURRENT_PIN_STATE; +// } +// } + +// fn push_char(letter: char) { +// unsafe { +// let index = CHAR_QUEUE.write_position as usize; +// let queue = &mut CHAR_QUEUE; +// queue.queue[index] = letter; +// CHAR_QUEUE.write_position = (CHAR_QUEUE.write_position + 1) % CHAR_QUEUE_LENTGH; +// queue.length += 1; +// } +// } + +// fn pop_char() -> char { +// unsafe { +// if CHAR_QUEUE.length == 0 { +// return '\r'; // Use CR to indicate empty queue +// } +// let return_value = CHAR_QUEUE.queue[CHAR_QUEUE.read_position]; +// CHAR_QUEUE.read_position = (CHAR_QUEUE.read_position + 1) % CHAR_QUEUE_LENTGH; +// CHAR_QUEUE.length -= 1; +// return return_value; +// } +// } + +// fn push_state(state: PinControlDescriptor) { +// unsafe { +// let index = PIN_CONTROL_QUEUE.write_position as usize; +// let queue = &mut PIN_CONTROL_QUEUE; +// queue.queue[index] = state; +// PIN_CONTROL_QUEUE.write_position = +// (PIN_CONTROL_QUEUE.write_position + 1) % STATE_QUEUE_LENGTH; +// queue.length += 1; +// } +// } + +// fn pop_state() -> PinControlDescriptor { +// unsafe { +// if PIN_CONTROL_QUEUE.length == 0 { +// return PinControlDescriptor { +// pin_state: false, +// duration: 1, +// }; +// } +// let return_value = &PIN_CONTROL_QUEUE.queue[PIN_CONTROL_QUEUE.read_position]; +// PIN_CONTROL_QUEUE.length -= 1; +// PIN_CONTROL_QUEUE.read_position = +// (PIN_CONTROL_QUEUE.read_position + 1) % STATE_QUEUE_LENGTH; +// return PinControlDescriptor { +// pin_state: return_value.pin_state, +// duration: return_value.duration, +// }; +// } +// } + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Agilistas!") + .product("Serial Port Morse Emitter") + .serial_number("TRINKY_MORSE") + .device_class(USB_CLASS_CDC) + .build(), + ); + // USB_BUS.event_queue = Some(&mut USB_BUS_EVENTS); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); + timer.start(3.mhz()); + let neo_pixel = pins.neo_pixel.into_push_pull_output(); + let mut ws2812 = Ws2812::new(timer, neo_pixel); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + const NUM_LEDS: usize = 4; + let off = [RGB8::default(); NUM_LEDS]; + let on = [ + RGB8::new(5, 5, 0), + RGB8::new(0, 5, 5), + RGB8::new(5, 0, 5), + RGB8::new(2, 2, 2), + ]; + + loop { + // let letter = pop(); + // emit_morse_letter(letter); + let state = get_next_state(); + if state { + // turn on + ws2812.write(on.iter().cloned()).unwrap(); + } else { + // turn off + ws2812.write(off.iter().cloned()).unwrap(); + } + + // ws2812.write(off.iter().cloned()).unwrap(); + delay.delay_ms(INTERVAL); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + USB_BUS.as_mut().map(|usb_dev| { + USB_SERIAL.as_mut().map(|serial| { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write("Received: ".as_bytes()).ok(); + serial.write(&[c.clone()]).ok(); + // serial.write("\tCharacter queue size: ".as_bytes()).ok(); // Hmmm. How do I do this in embedded Rust? + // let queue_size = i32::from(CHAR_QUEUE.length); + // serial.write(queue_size.to_str().as_bytes()).ok(); + serial.write("\r\n".as_bytes()).ok(); + // emit_morse_letter('e'); + + let letter = char::from_u32([c.clone()][0] as u32).unwrap(); + push_char(letter); + // emit_morse_letter(letter); // TODO: Move this to next state)_ + } + }; + }); + }); + }; +} + +// fn emit_morse_letter(letter: char) { +// let downcased_letter = letter.to_ascii_lowercase(); // Add support for Latin 1 later. +// match downcased_letter { +// 'a' => { +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'b' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'c' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'd' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'e' => { +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'f' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'g' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'h' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'i' => { +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'j' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'k' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'l' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'm' => { +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'n' => { +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'o' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'p' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 'q' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'r' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 's' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// 't' => { +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'u' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'v' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'w' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'x' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'y' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// 'z' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '0' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '1' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '2' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '3' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '4' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '5' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '6' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '7' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '8' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '9' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// ' ' => { +// push_space(); +// } +// ',' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '.' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '?' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '\'' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '!' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '/' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '(' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// ')' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '&' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// ':' => { +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// ';' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '=' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '+' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '-' => { +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '_' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// push_letter_interval(); +// } +// '"' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '$' => { +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '@' => { +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dash(); +// emit_morse_dot(); +// emit_morse_dash(); +// emit_morse_dot(); +// push_letter_interval(); +// } +// '\r' => { +// // We use CR to indicate the quueue is empty, so we make it phs only one blank cycle. +// push_letter_interval(); +// } +// _ => { +// emit_morse_space(); +// } +// } +// } + +// fn emit_morse_dot() { +// push_dot(); +// } +// fn emit_morse_dash() { +// push_dash(); +// } +// fn emit_morse_space() { +// push_space(); +// } + +#[interrupt] +fn USB() { + // Note: USB is the name of the interrupt, you can not attach the #[interrupt] + // tag to poll_usb. Although you could add the contents of poll_usb into + // this function, separating them allows you to add more functions to run on + // the USB interrupt in the future. + poll_usb(); +} + +fn print_serial_header() { + unsafe { + let serial = USB_SERIAL.as_mut().unwrap(); + serial + .write("Connected to Neo Trinky Morse Echo.\r\n: ".as_bytes()) + .ok(); + serial + .write( + "Type letters and the LED will emit their morse code equivalents.\r\n: ".as_bytes(), + ) + .ok(); + } +} diff --git a/boards/xiao_m0/README.md b/boards/xiao_m0/README.md index 8df3577410a4..5fc79bf84fff 100644 --- a/boards/xiao_m0/README.md +++ b/boards/xiao_m0/README.md @@ -3,6 +3,10 @@ This crate provides a type-safe API for working with the [Seeed Studio Seeeduino XIAO](http://wiki.seeedstudio.com/Seeeduino-XIAO/). +## FORK NOTES: + +cargo hf2 --release --example usb_morse --features="unproven" + ## Prerequisites * Install the cross compile toolchain `rustup target add thumbv6m-none-eabi` diff --git a/boards/xiao_m0/examples/milspec_blink.rs b/boards/xiao_m0/examples/milspec_blink.rs new file mode 100644 index 000000000000..a779a5ed5b14 --- /dev/null +++ b/boards/xiao_m0/examples/milspec_blink.rs @@ -0,0 +1,34 @@ +#![no_main] +#![no_std] + +extern crate panic_halt; + +use hal::{clock::GenericClockController, delay::Delay, prelude::*}; +use pac::{CorePeripherals, Peripherals}; + +use bsp::{entry, hal, pac, Led0}; +use xiao_m0 as bsp; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let mut delay = Delay::new(core.SYST, &mut clocks); + let mut led0: Led0 = pins.led0.into_push_pull_output(); + + loop { + delay.delay_ms(100u8); + led0.toggle().unwrap(); + delay.delay_ms(200u8); + delay.delay_ms(100u8); + led0.toggle().unwrap(); + } +} diff --git a/boards/xiao_m0/examples/usb_morse.rs b/boards/xiao_m0/examples/usb_morse.rs new file mode 100644 index 000000000000..aab32641f4e1 --- /dev/null +++ b/boards/xiao_m0/examples/usb_morse.rs @@ -0,0 +1,99 @@ +#![no_std] +#![no_main] + +extern crate panic_halt; + +use cortex_m::{asm::delay as asm_delay, peripheral::NVIC}; +use hal::{clock::GenericClockController, prelude::*, usb::UsbBus}; +use pac::{interrupt, CorePeripherals, Peripherals}; +use usb_device::{bus::UsbBusAllocator, prelude::*}; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use bsp::{entry, hal, pac, Led0, Led1}; +use xiao_m0 as bsp; + +struct UsbSerial { + usb_bus: UsbBusAllocator, + serial: SerialPort<'static, UsbBus>, + led_data: Led1, +} + +struct Environment { + usb_serial: Option, + led0: Led0, + peripherals: Peripherals, + core: CorePeripherals, + clocks: GenericClockController, + pins: bsp::Pins, + bus_allocator: UsbBusAllocator, +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; +static mut LED_DATA: Option = None; + +fn init() -> Environment { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + pins.usb_dm, + pins.usb_dp, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + let usb_serial = unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0xdead, 0xbeef)) + .manufacturer("Agilistas!") + .product("xiao_usb_morse") + .serial_number("1") + .device_class(USB_CLASS_CDC) + .build(), + ); + LED_DATA = Some(pins.led1.into_mode()); + UsbSerial { + usb_bus: USB_ALLOCATOR.as_ref().unwrap(), + serial: USB_SERIAL.as_ref().unwrap(), + led_data: LED_DATA.as_ref().unwrap(), + } + }; + + Environment { + usb_serial: Some(usb_serial), + led0: pins.led0.into_push_pull_output(), + peripherals, + core, + clocks, + pins, + bus_allocator, + } +} + +#[entry] +fn main() -> ! { + let mut environment: Environment = init(); + let mut led0: Led0 = environment.pins.led0.into_push_pull_output(); + let mut delay = hal::delay::Delay::new(environment.core.SYST, &mut environment.clocks); + + loop { + delay.delay_ms(200u8); + led0.toggle().unwrap(); + delay.delay_ms(600u16); + led0.toggle().unwrap(); + } +}