From 22d9778d3628d8ca1029e879b1f648218c630c04 Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Fri, 14 Mar 2025 17:22:01 +1300 Subject: [PATCH 01/15] First stab at a management tool Still a few TODOs before this can really be put to use: * Tool needs to populate examples section of BSP Cargo.toml * Move examples from BSPs to the examples directory * Handle support files for examples * Think about CI and whether we want to leave the BSP example dir populated --- Cargo.toml | 1 + examples/README.md | 14 +++ examples/examples.toml | 5 + examples/generic-usb_echo2.rs | 100 ++++++++++++++++++ examples/metro_m4-neopixel_rainbow.rs | 61 +++++++++++ manage/Cargo.toml | 10 ++ manage/src/error.rs | 43 ++++++++ manage/src/example.rs | 145 ++++++++++++++++++++++++++ manage/src/main.rs | 29 ++++++ 9 files changed, 408 insertions(+) create mode 100644 examples/README.md create mode 100644 examples/examples.toml create mode 100644 examples/generic-usb_echo2.rs create mode 100644 examples/metro_m4-neopixel_rainbow.rs create mode 100644 manage/Cargo.toml create mode 100644 manage/src/error.rs create mode 100644 manage/src/example.rs create mode 100644 manage/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 6613515bbc01..4d9c8027ed4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "atsamd-hal-macros", "pac/*", "boards/*", + "manage", ] [profile.dev] diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000000..28dc1223c2cb --- /dev/null +++ b/examples/README.md @@ -0,0 +1,14 @@ +# BSP Examples +This directory contains source files of examples provided with the Board Support +Packages (BSPs). + +These are distributed to the BSP crates using the `manage` tool, which does some +simple transformation to e.g. replace `{{bsp}}` in the example names with the +specific BSP as it is copied to the BSP crate. `manage` is configured through +`examples.toml`, which can be used as a reference to see which BSPs support +generic examples. + +## Naming +Example files are named using `bsp-example_name.rs`, where `bsp` is either a +specific BSP crate name, or `generic` to indicate that `examples.toml` will +contain a list of BSPs that support the example. diff --git a/examples/examples.toml b/examples/examples.toml new file mode 100644 index 000000000000..83606acdfdb8 --- /dev/null +++ b/examples/examples.toml @@ -0,0 +1,5 @@ +# Config file used by `manage` to distribute examples to the BSPs + +[examples.usb_echo2] +boards = ["metro_m0", "metro_m4"] +required-features = ["usb"] \ No newline at end of file diff --git a/examples/generic-usb_echo2.rs b/examples/generic-usb_echo2.rs new file mode 100644 index 000000000000..0cda68f20a13 --- /dev/null +++ b/examples/generic-usb_echo2.rs @@ -0,0 +1,100 @@ +#![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 bsp::hal; +use bsp::pac; +use {{bsp}} as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::prelude::*; +use hal::usb::UsbBus; +use pac::{interrupt, CorePeripherals, Peripherals}; + +#[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 mut red_led: bsp::RedLed = pins.d13.into(); + + 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)) + .strings(&[StringDescriptors::new(LangID::EN) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .expect("Failed to set strings") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + // Flash the LED in a spin loop to demonstrate that USB is + // entirely interrupt driven. + loop { + cycle_delay(15 * 1024 * 1024); + red_led.toggle().ok(); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + 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(&[*c]).ok(); + } + }; + }; + } + }; +} + +#[interrupt] +fn USB() { + poll_usb(); +} diff --git a/examples/metro_m4-neopixel_rainbow.rs b/examples/metro_m4-neopixel_rainbow.rs new file mode 100644 index 000000000000..1a422fd92c89 --- /dev/null +++ b/examples/metro_m4-neopixel_rainbow.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +use metro_m4 as bsp; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::ehal::delay::DelayNs; +use hal::time::Hertz; +use hal::timer::TimerCounter; +use hal::timer_traits::InterruptDrivenTimer; +use pac::{CorePeripherals, Peripherals}; + +use smart_leds::{ + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; +use ws2812_timer_delay as ws2812; + +#[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.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + let gclk0 = clocks.gclk0(); + let tc2_3 = clocks.tc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&tc2_3, peripherals.tc3, &mut peripherals.mclk); + timer.start(Hertz::MHz(3).into_duration()); + + let pins = bsp::Pins::new(peripherals.port); + let neopixel_pin = pins.neopixel.into_push_pull_output(); + let mut neopixel = ws2812::Ws2812::new(timer, neopixel_pin); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + for j in 0..255u8 { + let colors = [hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 32, + })]; + neopixel.write(colors.iter().cloned()).unwrap(); + delay.delay_ms(5); + } + } +} diff --git a/manage/Cargo.toml b/manage/Cargo.toml new file mode 100644 index 000000000000..585909df6a17 --- /dev/null +++ b/manage/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "manage" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5.32", features = ["derive"] } +handlebars = "6.3.1" +thiserror = "2.0.12" +toml = "0.8.23" diff --git a/manage/src/error.rs b/manage/src/error.rs new file mode 100644 index 000000000000..52e4765d9cde --- /dev/null +++ b/manage/src/error.rs @@ -0,0 +1,43 @@ +//! Error handling types for atsamd-rs/atsamd management program + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("IO error: {0}")] + Io(std::io::Error), + #[error("TOML deserializing error: {0}")] + Toml(toml::de::Error), + #[error("Handlebars rendering error: {0}")] + HBRender(handlebars::RenderError), + #[error("Handlebars template error: {0}")] + HBTemplate(handlebars::TemplateError), + #[error("{0}")] + Other(String), +} + +pub type Result = std::result::Result; + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: toml::de::Error) -> Self { + Error::Toml(err) + } +} + +impl From for Error { + fn from(err: handlebars::RenderError) -> Self { + Error::HBRender(err) + } +} + +impl From for Error { + fn from(err: handlebars::TemplateError) -> Self { + Error::HBTemplate(err) + } +} diff --git a/manage/src/example.rs b/manage/src/example.rs new file mode 100644 index 000000000000..0c9c1d826b3c --- /dev/null +++ b/manage/src/example.rs @@ -0,0 +1,145 @@ +//! Handling of BSP examples + +use crate::error::{Error, Result}; +use clap::Subcommand; +use handlebars::Handlebars; +use std::collections::BTreeMap; +use std::fs::{File, read_dir, read_to_string}; +use std::io::Write; +use std::path::PathBuf; +use toml::Table; + +#[derive(Debug, Subcommand)] +pub enum Commands { + /// Distribute examples amongst BSPs + Distribute { + /// Path to the examples + examples: String, + /// Path to the BSPs + bsps: String, + }, +} + +/// Entry point for example management +pub fn run(commands: &Commands) -> Result<()> { + match commands { + Commands::Distribute { examples, bsps } => distribute(examples, bsps), + } +} + +fn distribute(examples: &String, bsps: &String) -> Result<()> { + let toml = read_to_string(PathBuf::from(examples).join("examples.toml"))?; + + let examples_toml = toml.parse::()?; + + // TODO error out if this isn't a directory + let bsps_path = PathBuf::from(bsps); + + // Filter the example directory contents to get files with "rs" extension + for rust_source_path in read_dir(examples)?.filter_map(|entry| { + if let Ok(entry) = entry { + let path = entry.path(); + if !path.is_file() { + return None; + } + if let Some(extension) = path.extension() { + if extension == "rs" { + return Some(path); + } + } + } + return None; + }) { + // Above filter means we know this is a file and therefore has a name: + let source_name = rust_source_path.file_name().unwrap(); + + // ...but perhaps it's not a UTF-8 name (required as it selects from TOML) + let example_target_name = + rust_source_path + .file_stem() + .unwrap() + .to_str() + .ok_or(Error::Other(format!( + "Non-UTF8 characters detected in {:?}", + rust_source_path + )))?; + + // We split the example names on the first hyphen, to determine whether + // they're generic (and need to refer to example.toml for destinations) + // or specific to just one BSP. + + let parts: Vec<&str> = example_target_name.splitn(2, "-").collect(); + + if parts.len() != 2 { + return Err(Error::Other(format!( + "Example file {} doesn't conform to naming conventions", + source_name.to_string_lossy() + ))); + } + + let target = parts[0]; + let example_name = parts[1]; + + let is_generic = target == "generic"; + + let example_config = examples_toml + .get("examples") + .and_then(|list| list.get(example_name)); + + let boards = if is_generic { + let toml_array = example_config + .and_then(|c| c.get("boards").and_then(|a| a.as_array())) + .ok_or(Error::Other(format!( + "examples.toml entry for generic example `{example_name}` doesn't have a `boards` array" + )))?; + + let mut boards = Vec::new(); + for entry in toml_array { + if let Some(s) = entry.as_str() { + boards.push(s) + } else { + return Err(Error::Other(format!( + "Non-string entry in `boards` array for {example_name}: {:?}", + entry + ))); + } + } + boards + } else { + vec![target] + }; + + // Handlebars is designed around storing multiple templates at a time, + // but here there doesn't really seem to be a need for that. + let mut handlebars = Handlebars::new(); + + let source = read_to_string(&rust_source_path)?; + handlebars.register_template_string(example_name, source)?; + + for board in boards { + if board.is_empty() { + return Err(Error::Other(format!("Empty board name for {example_name}"))); + } + + let mut data = BTreeMap::new(); + data.insert("bsp".to_string(), board); + + let rendered = handlebars.render(example_name, &data)?; + + // TODO make the examples directory + + let rendered_path = bsps_path + .join(PathBuf::from(board)) + .join(PathBuf::from("examples")) + .join(PathBuf::from(example_name).with_extension("rs")); + + let mut filebuf = File::create(rendered_path)?; + filebuf.write_all(rendered.as_bytes())?; + + // TODO append to the BSP's Cargo.toml + // Same for README.md? + } + } + + Ok(()) +} diff --git a/manage/src/main.rs b/manage/src/main.rs new file mode 100644 index 000000000000..d62b43c0febe --- /dev/null +++ b/manage/src/main.rs @@ -0,0 +1,29 @@ +//! Tool for managing atsamd-rs/atsamd + +mod error; +mod example; +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(version, about = "Manages atsamd-rs/atsamd", long_about = None)] +#[command(propagate_version = true)] +pub struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Manage examples provided for BSPs + #[command(subcommand)] + Example(example::Commands), +} + +fn main() { + let cli = Cli::parse(); + + match &cli.command { + Commands::Example(example_commands) => example::run(example_commands), + } + .unwrap(); +} From f372922d7db3f30554bbcb02a21c0df6d7454366 Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Thu, 27 Mar 2025 20:21:43 +1300 Subject: [PATCH 02/15] directly copy specific examples, improve errors This avoids issues with some specific examples that use macro definitions containing `{{`, which handlebars uses as a delimiter. --- examples/examples.toml | 4 -- examples/generic-usb_echo2.rs | 100 ---------------------------------- manage/src/error.rs | 2 + manage/src/example.rs | 41 +++++++++----- manage/src/main.rs | 13 ++++- 5 files changed, 40 insertions(+), 120 deletions(-) delete mode 100644 examples/generic-usb_echo2.rs diff --git a/examples/examples.toml b/examples/examples.toml index 83606acdfdb8..66e3b39edeed 100644 --- a/examples/examples.toml +++ b/examples/examples.toml @@ -1,5 +1 @@ # Config file used by `manage` to distribute examples to the BSPs - -[examples.usb_echo2] -boards = ["metro_m0", "metro_m4"] -required-features = ["usb"] \ No newline at end of file diff --git a/examples/generic-usb_echo2.rs b/examples/generic-usb_echo2.rs deleted file mode 100644 index 0cda68f20a13..000000000000 --- a/examples/generic-usb_echo2.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![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 bsp::hal; -use bsp::pac; -use {{bsp}} as bsp; - -use bsp::entry; -use hal::clock::GenericClockController; -use hal::prelude::*; -use hal::usb::UsbBus; -use pac::{interrupt, CorePeripherals, Peripherals}; - -#[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 mut red_led: bsp::RedLed = pins.d13.into(); - - 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)) - .strings(&[StringDescriptors::new(LangID::EN) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST")]) - .expect("Failed to set strings") - .device_class(USB_CLASS_CDC) - .build(), - ); - } - - unsafe { - core.NVIC.set_priority(interrupt::USB, 1); - NVIC::unmask(interrupt::USB); - } - - // Flash the LED in a spin loop to demonstrate that USB is - // entirely interrupt driven. - loop { - cycle_delay(15 * 1024 * 1024); - red_led.toggle().ok(); - } -} - -static mut USB_ALLOCATOR: Option> = None; -static mut USB_BUS: Option> = None; -static mut USB_SERIAL: Option> = None; - -fn poll_usb() { - unsafe { - if let Some(usb_dev) = USB_BUS.as_mut() { - if let Some(serial) = USB_SERIAL.as_mut() { - 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(&[*c]).ok(); - } - }; - }; - } - }; -} - -#[interrupt] -fn USB() { - poll_usb(); -} diff --git a/manage/src/error.rs b/manage/src/error.rs index 52e4765d9cde..31f4027c2261 100644 --- a/manage/src/error.rs +++ b/manage/src/error.rs @@ -12,6 +12,8 @@ pub enum Error { HBRender(handlebars::RenderError), #[error("Handlebars template error: {0}")] HBTemplate(handlebars::TemplateError), + #[error("Failure aleady logged to stderr")] + Logged, #[error("{0}")] Other(String), } diff --git a/manage/src/example.rs b/manage/src/example.rs index 0c9c1d826b3c..4fe922ea3b88 100644 --- a/manage/src/example.rs +++ b/manage/src/example.rs @@ -4,8 +4,8 @@ use crate::error::{Error, Result}; use clap::Subcommand; use handlebars::Handlebars; use std::collections::BTreeMap; -use std::fs::{File, read_dir, read_to_string}; -use std::io::Write; +use std::fs::{copy, File, read_dir, read_to_string}; +use std::io::{Write}; use std::path::PathBuf; use toml::Table; @@ -113,19 +113,20 @@ fn distribute(examples: &String, bsps: &String) -> Result<()> { // but here there doesn't really seem to be a need for that. let mut handlebars = Handlebars::new(); - let source = read_to_string(&rust_source_path)?; - handlebars.register_template_string(example_name, source)?; + if is_generic { + let source = read_to_string(&rust_source_path)?; + handlebars.register_template_string(example_name, source).map_err(|err| { + eprintln!("Error while rendering {example_name} for {:?}:", boards); + eprintln!("{}", err); + Error::Logged + })?; + } for board in boards { if board.is_empty() { return Err(Error::Other(format!("Empty board name for {example_name}"))); } - let mut data = BTreeMap::new(); - data.insert("bsp".to_string(), board); - - let rendered = handlebars.render(example_name, &data)?; - // TODO make the examples directory let rendered_path = bsps_path @@ -133,11 +134,23 @@ fn distribute(examples: &String, bsps: &String) -> Result<()> { .join(PathBuf::from("examples")) .join(PathBuf::from(example_name).with_extension("rs")); - let mut filebuf = File::create(rendered_path)?; - filebuf.write_all(rendered.as_bytes())?; - - // TODO append to the BSP's Cargo.toml - // Same for README.md? + if is_generic { + let mut data = BTreeMap::new(); + data.insert("bsp".to_string(), board); + + let rendered = handlebars.render(example_name, &data).map_err(|err| { + eprintln!("Error while rendering {example_name} for {board}:"); + eprintln!("{}", err.reason()); + Error::HBRender(err) + })?; + + // TODO if there's an existing file, compare it for equality and error out if we'd change the file + + let mut filebuf = File::create(rendered_path)?; + filebuf.write_all(rendered.as_bytes())?; + } else { + copy(&rust_source_path, rendered_path)?; + } } } diff --git a/manage/src/main.rs b/manage/src/main.rs index d62b43c0febe..06e45a40b5d5 100644 --- a/manage/src/main.rs +++ b/manage/src/main.rs @@ -3,6 +3,7 @@ mod error; mod example; use clap::{Parser, Subcommand}; +use std::process::exit; #[derive(Parser)] #[command(version, about = "Manages atsamd-rs/atsamd", long_about = None)] @@ -22,8 +23,16 @@ pub enum Commands { fn main() { let cli = Cli::parse(); - match &cli.command { + match match &cli.command { Commands::Example(example_commands) => example::run(example_commands), + } { + Ok(()) => {} + Err(error::Error::Logged) => { + exit(1); + } + Err(err) => { + eprintln!("Command failed with error: {}", err); + exit(2); + } } - .unwrap(); } From cad7fd95d726c2bb57c4d968197033fde2960733 Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Fri, 28 Mar 2025 20:54:35 +1300 Subject: [PATCH 03/15] Temporary script to copy examples --- boards/doit.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 boards/doit.sh diff --git a/boards/doit.sh b/boards/doit.sh new file mode 100755 index 000000000000..3022242a4159 --- /dev/null +++ b/boards/doit.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set +e + +for b in *; do + [ "$b" == "$(basename "$0")" ] && continue + for e in "$b"/examples/*.rs; do + newname="$b"-$(basename "$e") + cp "$e" ../examples/"$newname" + done +done \ No newline at end of file From 41e3bf02299d6cbf2f6cab22e2edf02a2c60f63c Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Thu, 27 Mar 2025 20:23:02 +1300 Subject: [PATCH 04/15] Add current set of examples --- examples/arduino_mkr1000-blinky_basic.rs | 38 + examples/arduino_mkrvidor4000-blinky_basic.rs | 38 + ...no_mkrvidor4000-enable_battery_charging.rs | 61 + examples/arduino_mkrvidor4000-run_fpga.rs | 33 + examples/arduino_mkrzero-blinky_basic.rs | 38 + examples/arduino_mkrzero-blinky_rtic.rs | 73 ++ examples/arduino_mkrzero-pwm.rs | 54 + examples/arduino_mkrzero-usb_logging.rs | 105 ++ examples/arduino_nano33iot-blinky_basic.rs | 38 + examples/arduino_nano33iot-i2c_ssd1306.rs | 85 ++ examples/arduino_nano33iot-serial.rs | 47 + examples/arduino_nano33iot-spi_st7735.rs | 82 ++ examples/arduino_nano33iot-usb_logging.rs | 108 ++ examples/atsame54_xpro-blinky_basic.rs | 42 + examples/atsame54_xpro-blinky_rtic.rs | 83 ++ examples/atsame54_xpro-mcan.rs | 319 ++++++ ...circuit_playground_express-blinky_basic.rs | 38 + ...uit_playground_express-neopixel_rainbow.rs | 67 ++ examples/circuit_playground_express-uart.rs | 66 ++ .../circuit_playground_express-usb_serial.rs | 103 ++ examples/edgebadge-blinky_basic.rs | 43 + examples/edgebadge-button_rtic.rs | 79 ++ examples/edgebadge-ferris_img.rs | 63 ++ examples/edgebadge-neopixel_adc_battery.rs | 82 ++ examples/edgebadge-neopixel_adc_light.rs | 81 ++ examples/edgebadge-neopixel_button.rs | 89 ++ examples/edgebadge-neopixel_easing.rs | 87 ++ examples/edgebadge-neopixel_rainbow_timer.rs | 77 ++ examples/edgebadge-neopixel_tilt.rs | 129 +++ examples/edgebadge-usb_poll.rs | 77 ++ examples/edgebadge-usb_serial.rs | 132 +++ examples/feather_m0-adalogger.rs | 209 ++++ examples/feather_m0-adc.rs | 53 + examples/feather_m0-async_dmac.rs | 73 ++ examples/feather_m0-async_eic.rs | 59 + examples/feather_m0-async_i2c.rs | 74 ++ examples/feather_m0-async_spi.rs | 80 ++ examples/feather_m0-async_timer.rs | 56 + examples/feather_m0-async_uart.rs | 101 ++ examples/feather_m0-blinky_basic.rs | 38 + examples/feather_m0-blinky_embassy.rs | 55 + examples/feather_m0-blinky_rtic.rs | 77 ++ examples/feather_m0-clock.rs | 189 ++++ examples/feather_m0-dmac.rs | 103 ++ examples/feather_m0-eic.rs | 90 ++ examples/feather_m0-i2c.rs | 70 ++ examples/feather_m0-pwm.rs | 56 + examples/feather_m0-sleeping_timer.rs | 122 ++ examples/feather_m0-sleeping_timer_rtc.rs | 117 ++ examples/feather_m0-spi.rs | 83 ++ ...ther_m0-ssd1306_graphicsmode_128x32_i2c.rs | 142 +++ ...ther_m0-ssd1306_graphicsmode_128x64_i2c.rs | 164 +++ ...ther_m0-ssd1306_graphicsmode_128x64_spi.rs | 157 +++ ...ther_m0-ssd1306_terminalmode_128x32_i2c.rs | 107 ++ ...ther_m0-ssd1306_terminalmode_128x64_i2c.rs | 131 +++ ...ther_m0-ssd1306_terminalmode_128x64_spi.rs | 124 ++ examples/feather_m0-timers.rs | 50 + examples/feather_m0-uart.rs | 73 ++ examples/feather_m0-uart_dma_blocking.rs | 82 ++ examples/feather_m0-uart_dma_nonblocking.rs | 100 ++ examples/feather_m0-usb_echo.rs | 103 ++ examples/feather_m4-blinky_basic.rs | 37 + examples/feather_m4-blinky_rtic.rs | 85 ++ examples/feather_m4-clocking_v2.rs | 165 +++ examples/feather_m4-dmac.rs | 103 ++ examples/feather_m4-i2c.rs | 72 ++ examples/feather_m4-neopixel_rainbow.rs | 69 ++ examples/feather_m4-nvm_dsu.rs | 244 ++++ examples/feather_m4-pukcc_test.rs | 1000 +++++++++++++++++ examples/feather_m4-pwm.rs | 57 + examples/feather_m4-serial.rs | 57 + examples/feather_m4-sleeping_timer_rtc.rs | 91 ++ examples/feather_m4-smart_eeprom.rs | 227 ++++ examples/feather_m4-spi.rs | 83 ++ examples/feather_m4-timers.rs | 54 + examples/feather_m4-trng.rs | 49 + examples/feather_m4-uart.rs | 72 ++ examples/feather_m4-uart_dma_blocking.rs | 83 ++ examples/feather_m4-uart_dma_nonblocking.rs | 101 ++ examples/feather_m4-uart_poll_echo.rs | 80 ++ examples/feather_m4-usb_echo.rs | 118 ++ examples/gemma_m0-blinky_basic.rs | 37 + examples/grand_central_m4-blinky_basic.rs | 41 + examples/grand_central_m4-eic.rs | 100 ++ examples/grand_central_m4-i2c.rs | 72 ++ examples/grand_central_m4-neopixel_rainbow.rs | 61 + examples/grand_central_m4-usb_serial.rs | 150 +++ examples/itsybitsy_m0-adc.rs | 41 + examples/itsybitsy_m0-blinky_basic.rs | 38 + examples/itsybitsy_m0-blinky_rtic.rs | 72 ++ examples/itsybitsy_m0-clock.rs | 187 +++ examples/itsybitsy_m0-dmac.rs | 104 ++ examples/itsybitsy_m0-dotstar_flashing.rs | 66 ++ examples/itsybitsy_m0-dotstar_rainbow.rs | 84 ++ examples/itsybitsy_m0-eic.rs | 90 ++ examples/itsybitsy_m0-pwm.rs | 52 + examples/itsybitsy_m0-sleeping_timer.rs | 120 ++ examples/itsybitsy_m0-sleeping_timer_rtc.rs | 115 ++ ...itsy_m0-ssd1306_graphicsmode_128x64_i2c.rs | 163 +++ ...itsy_m0-ssd1306_graphicsmode_128x64_spi.rs | 155 +++ ...itsy_m0-ssd1306_terminalmode_128x64_i2c.rs | 130 +++ ...itsy_m0-ssd1306_terminalmode_128x64_spi.rs | 122 ++ examples/itsybitsy_m0-timers.rs | 50 + examples/itsybitsy_m0-twitching_usb_mouse.rs | 114 ++ examples/itsybitsy_m0-uart.rs | 106 ++ examples/itsybitsy_m0-usb_echo.rs | 101 ++ examples/itsybitsy_m4-blinky_basic.rs | 46 + examples/itsybitsy_m4-dotstar.rs | 62 + examples/itsybitsy_m4-sercom_interrupt.rs | 157 +++ examples/itsybitsy_m4-spi.rs | 75 ++ examples/itsybitsy_m4-usb_serial.rs | 166 +++ examples/matrix_portal_m4-blinky_basic.rs | 35 + examples/matrix_portal_m4-pwm.rs | 57 + examples/metro_m0-blinky_basic.rs | 38 + examples/metro_m0-blinky_rtic.rs | 82 ++ examples/metro_m0-i2c.rs | 70 ++ examples/metro_m0-spi.rs | 84 ++ examples/metro_m0-usb_echo.rs | 100 ++ examples/metro_m4-adc.rs | 58 + examples/metro_m4-async_dmac.rs | 74 ++ examples/metro_m4-async_eic.rs | 57 + examples/metro_m4-async_i2c.rs | 84 ++ examples/metro_m4-async_spi.rs | 88 ++ examples/metro_m4-async_timer.rs | 49 + examples/metro_m4-async_uart.rs | 111 ++ examples/metro_m4-blinky_basic.rs | 41 + examples/metro_m4-blinky_rtic.rs | 85 ++ examples/metro_m4-clock_out.rs | 42 + examples/metro_m4-hello.rs | 34 + examples/metro_m4-i2c.rs | 72 ++ examples/metro_m4-neopixel_blink.rs | 60 + examples/metro_m4-pwm.rs | 51 + examples/metro_m4-serial.rs | 57 + examples/metro_m4-spi.rs | 82 ++ examples/metro_m4-timer.rs | 50 + examples/metro_m4-trng.rs | 40 + examples/metro_m4-usb_logging.rs | 130 +++ examples/neo_trinkey-blinky_basic.rs | 56 + examples/neo_trinkey-blinky_rainbow.rs | 68 ++ examples/neo_trinkey-usb_ack.rs | 99 ++ examples/neokey_trinkey-blinky.rs | 60 + examples/neokey_trinkey-button.rs | 52 + examples/neokey_trinkey-rainbow.rs | 71 ++ examples/neokey_trinkey-usb_echo.rs | 99 ++ examples/p1am_100-adc.rs | 39 + examples/p1am_100-blinky_basic.rs | 37 + examples/p1am_100-clock.rs | 187 +++ examples/p1am_100-pwm.rs | 107 ++ examples/p1am_100-sleeping_timer.rs | 110 ++ examples/p1am_100-sleeping_timer_rtc.rs | 105 ++ examples/p1am_100-timers.rs | 46 + examples/p1am_100-uart_echo_rtic.rs | 86 ++ examples/p1am_100-usb_echo.rs | 104 ++ examples/p1am_100-usb_echo_rtic.rs | 111 ++ examples/pfza_proto1-blinky_basic.rs | 38 + examples/pygamer-blinky_basic.rs | 45 + examples/pygamer-blinky_rtic.rs | 83 ++ examples/pygamer-button_rtic.rs | 81 ++ examples/pygamer-clock_out.rs | 36 + examples/pygamer-ferris_img.rs | 65 ++ examples/pygamer-neopixel_adc_battery.rs | 89 ++ examples/pygamer-neopixel_adc_light.rs | 88 ++ examples/pygamer-neopixel_button.rs | 115 ++ examples/pygamer-neopixel_easing.rs | 102 ++ examples/pygamer-neopixel_rainbow.rs | 84 ++ examples/pygamer-pwm_tc4.rs | 63 ++ examples/pygamer-pwm_tcc0.rs | 71 ++ examples/pygamer-qspi.rs | 118 ++ examples/pygamer-sd_card.rs | 139 +++ examples/pygamer-timer.rs | 58 + examples/pygamer-usb_poll.rs | 88 ++ examples/pyportal-blinky_basic.rs | 49 + examples/pyportal-ili9341_240x320.rs | 101 ++ examples/pyportal-neopixel_rainbow.rs | 61 + examples/pyportal-usb_echo.rs | 124 ++ examples/qt_py_m0-neopixel.rs | 73 ++ examples/qt_py_m0-usb_echo.rs | 91 ++ examples/samd11_bare-adc.rs | 54 + examples/samd11_bare-blinky_basic.rs | 37 + examples/samd11_bare-blinky_rtic.rs | 82 ++ examples/samd11_bare-i2c.rs | 70 ++ examples/samd11_bare-pwm.rs | 56 + examples/samd11_bare-serial.rs | 69 ++ examples/samd11_bare-timer.rs | 48 + examples/samd21_mini-serial.rs | 116 ++ examples/serpente-blinky_basic.rs | 39 + examples/serpente-pwm.rs | 85 ++ examples/sodaq_one-blinky.rs | 55 + examples/sodaq_sara_aff-blinky_basic.rs | 37 + examples/trellis_m4-neopixel_accel.rs | 71 ++ examples/trellis_m4-neopixel_blink.rs | 53 + examples/trellis_m4-neopixel_keypad.rs | 93 ++ examples/trellis_m4-neopixel_orientation.rs | 110 ++ examples/trellis_m4-neopixel_rainbow.rs | 61 + examples/trinket_m0-blinky_basic.rs | 33 + examples/trinket_m0-dotstar.rs | 64 ++ examples/trinket_m0-eic.rs | 47 + examples/trinket_m0-pwm.rs | 47 + examples/trinket_m0-usb_serial.rs | 97 ++ examples/trinket_m0-watchdog.rs | 71 ++ examples/wio_lite_mg126-adc.rs | 38 + examples/wio_lite_mg126-adc_usb.rs | 104 ++ examples/wio_lite_mg126-blinky_basic.rs | 37 + examples/wio_lite_mg126-pwm.rs | 50 + examples/wio_lite_w600-blinky_basic.rs | 39 + examples/wio_lite_w600-usb_ack.rs | 99 ++ examples/wio_terminal-blinky.rs | 35 + examples/wio_terminal-buttons.rs | 186 +++ examples/wio_terminal-clock.rs | 246 ++++ examples/wio_terminal-microphone.rs | 197 ++++ examples/wio_terminal-orientation.rs | 135 +++ examples/wio_terminal-qspi.rs | 278 +++++ examples/wio_terminal-sdcard.rs | 149 +++ examples/wio_terminal-snake.rs | 323 ++++++ examples/wio_terminal-usb_serial_display.rs | 242 ++++ examples/wio_terminal-wifi_connect.rs | 144 +++ examples/wio_terminal-wifi_scan.rs | 223 ++++ examples/xiao_m0-blink.rs | 31 + examples/xiao_m0-eic.rs | 113 ++ examples/xiao_m0-sercom_interrupt.rs | 147 +++ examples/xiao_m0-shared_i2c.rs | 56 + examples/xiao_m0-ssd1306_i2c.rs | 119 ++ examples/xiao_m0-usb_echo.rs | 96 ++ 223 files changed, 20731 insertions(+) create mode 100644 examples/arduino_mkr1000-blinky_basic.rs create mode 100644 examples/arduino_mkrvidor4000-blinky_basic.rs create mode 100644 examples/arduino_mkrvidor4000-enable_battery_charging.rs create mode 100644 examples/arduino_mkrvidor4000-run_fpga.rs create mode 100644 examples/arduino_mkrzero-blinky_basic.rs create mode 100644 examples/arduino_mkrzero-blinky_rtic.rs create mode 100644 examples/arduino_mkrzero-pwm.rs create mode 100644 examples/arduino_mkrzero-usb_logging.rs create mode 100644 examples/arduino_nano33iot-blinky_basic.rs create mode 100644 examples/arduino_nano33iot-i2c_ssd1306.rs create mode 100644 examples/arduino_nano33iot-serial.rs create mode 100644 examples/arduino_nano33iot-spi_st7735.rs create mode 100644 examples/arduino_nano33iot-usb_logging.rs create mode 100644 examples/atsame54_xpro-blinky_basic.rs create mode 100644 examples/atsame54_xpro-blinky_rtic.rs create mode 100644 examples/atsame54_xpro-mcan.rs create mode 100644 examples/circuit_playground_express-blinky_basic.rs create mode 100644 examples/circuit_playground_express-neopixel_rainbow.rs create mode 100644 examples/circuit_playground_express-uart.rs create mode 100644 examples/circuit_playground_express-usb_serial.rs create mode 100644 examples/edgebadge-blinky_basic.rs create mode 100644 examples/edgebadge-button_rtic.rs create mode 100644 examples/edgebadge-ferris_img.rs create mode 100644 examples/edgebadge-neopixel_adc_battery.rs create mode 100644 examples/edgebadge-neopixel_adc_light.rs create mode 100644 examples/edgebadge-neopixel_button.rs create mode 100644 examples/edgebadge-neopixel_easing.rs create mode 100644 examples/edgebadge-neopixel_rainbow_timer.rs create mode 100644 examples/edgebadge-neopixel_tilt.rs create mode 100644 examples/edgebadge-usb_poll.rs create mode 100644 examples/edgebadge-usb_serial.rs create mode 100644 examples/feather_m0-adalogger.rs create mode 100644 examples/feather_m0-adc.rs create mode 100644 examples/feather_m0-async_dmac.rs create mode 100644 examples/feather_m0-async_eic.rs create mode 100644 examples/feather_m0-async_i2c.rs create mode 100644 examples/feather_m0-async_spi.rs create mode 100644 examples/feather_m0-async_timer.rs create mode 100644 examples/feather_m0-async_uart.rs create mode 100644 examples/feather_m0-blinky_basic.rs create mode 100644 examples/feather_m0-blinky_embassy.rs create mode 100644 examples/feather_m0-blinky_rtic.rs create mode 100644 examples/feather_m0-clock.rs create mode 100644 examples/feather_m0-dmac.rs create mode 100644 examples/feather_m0-eic.rs create mode 100644 examples/feather_m0-i2c.rs create mode 100644 examples/feather_m0-pwm.rs create mode 100644 examples/feather_m0-sleeping_timer.rs create mode 100644 examples/feather_m0-sleeping_timer_rtc.rs create mode 100644 examples/feather_m0-spi.rs create mode 100644 examples/feather_m0-ssd1306_graphicsmode_128x32_i2c.rs create mode 100644 examples/feather_m0-ssd1306_graphicsmode_128x64_i2c.rs create mode 100644 examples/feather_m0-ssd1306_graphicsmode_128x64_spi.rs create mode 100644 examples/feather_m0-ssd1306_terminalmode_128x32_i2c.rs create mode 100644 examples/feather_m0-ssd1306_terminalmode_128x64_i2c.rs create mode 100644 examples/feather_m0-ssd1306_terminalmode_128x64_spi.rs create mode 100644 examples/feather_m0-timers.rs create mode 100644 examples/feather_m0-uart.rs create mode 100644 examples/feather_m0-uart_dma_blocking.rs create mode 100644 examples/feather_m0-uart_dma_nonblocking.rs create mode 100644 examples/feather_m0-usb_echo.rs create mode 100644 examples/feather_m4-blinky_basic.rs create mode 100644 examples/feather_m4-blinky_rtic.rs create mode 100644 examples/feather_m4-clocking_v2.rs create mode 100644 examples/feather_m4-dmac.rs create mode 100644 examples/feather_m4-i2c.rs create mode 100644 examples/feather_m4-neopixel_rainbow.rs create mode 100644 examples/feather_m4-nvm_dsu.rs create mode 100644 examples/feather_m4-pukcc_test.rs create mode 100644 examples/feather_m4-pwm.rs create mode 100644 examples/feather_m4-serial.rs create mode 100644 examples/feather_m4-sleeping_timer_rtc.rs create mode 100644 examples/feather_m4-smart_eeprom.rs create mode 100644 examples/feather_m4-spi.rs create mode 100644 examples/feather_m4-timers.rs create mode 100644 examples/feather_m4-trng.rs create mode 100644 examples/feather_m4-uart.rs create mode 100644 examples/feather_m4-uart_dma_blocking.rs create mode 100644 examples/feather_m4-uart_dma_nonblocking.rs create mode 100644 examples/feather_m4-uart_poll_echo.rs create mode 100644 examples/feather_m4-usb_echo.rs create mode 100644 examples/gemma_m0-blinky_basic.rs create mode 100644 examples/grand_central_m4-blinky_basic.rs create mode 100644 examples/grand_central_m4-eic.rs create mode 100644 examples/grand_central_m4-i2c.rs create mode 100644 examples/grand_central_m4-neopixel_rainbow.rs create mode 100644 examples/grand_central_m4-usb_serial.rs create mode 100644 examples/itsybitsy_m0-adc.rs create mode 100644 examples/itsybitsy_m0-blinky_basic.rs create mode 100644 examples/itsybitsy_m0-blinky_rtic.rs create mode 100644 examples/itsybitsy_m0-clock.rs create mode 100644 examples/itsybitsy_m0-dmac.rs create mode 100644 examples/itsybitsy_m0-dotstar_flashing.rs create mode 100644 examples/itsybitsy_m0-dotstar_rainbow.rs create mode 100644 examples/itsybitsy_m0-eic.rs create mode 100644 examples/itsybitsy_m0-pwm.rs create mode 100644 examples/itsybitsy_m0-sleeping_timer.rs create mode 100644 examples/itsybitsy_m0-sleeping_timer_rtc.rs create mode 100644 examples/itsybitsy_m0-ssd1306_graphicsmode_128x64_i2c.rs create mode 100644 examples/itsybitsy_m0-ssd1306_graphicsmode_128x64_spi.rs create mode 100644 examples/itsybitsy_m0-ssd1306_terminalmode_128x64_i2c.rs create mode 100644 examples/itsybitsy_m0-ssd1306_terminalmode_128x64_spi.rs create mode 100644 examples/itsybitsy_m0-timers.rs create mode 100644 examples/itsybitsy_m0-twitching_usb_mouse.rs create mode 100644 examples/itsybitsy_m0-uart.rs create mode 100644 examples/itsybitsy_m0-usb_echo.rs create mode 100644 examples/itsybitsy_m4-blinky_basic.rs create mode 100644 examples/itsybitsy_m4-dotstar.rs create mode 100644 examples/itsybitsy_m4-sercom_interrupt.rs create mode 100644 examples/itsybitsy_m4-spi.rs create mode 100644 examples/itsybitsy_m4-usb_serial.rs create mode 100644 examples/matrix_portal_m4-blinky_basic.rs create mode 100644 examples/matrix_portal_m4-pwm.rs create mode 100644 examples/metro_m0-blinky_basic.rs create mode 100644 examples/metro_m0-blinky_rtic.rs create mode 100644 examples/metro_m0-i2c.rs create mode 100644 examples/metro_m0-spi.rs create mode 100644 examples/metro_m0-usb_echo.rs create mode 100644 examples/metro_m4-adc.rs create mode 100644 examples/metro_m4-async_dmac.rs create mode 100644 examples/metro_m4-async_eic.rs create mode 100644 examples/metro_m4-async_i2c.rs create mode 100644 examples/metro_m4-async_spi.rs create mode 100644 examples/metro_m4-async_timer.rs create mode 100644 examples/metro_m4-async_uart.rs create mode 100644 examples/metro_m4-blinky_basic.rs create mode 100644 examples/metro_m4-blinky_rtic.rs create mode 100644 examples/metro_m4-clock_out.rs create mode 100644 examples/metro_m4-hello.rs create mode 100644 examples/metro_m4-i2c.rs create mode 100644 examples/metro_m4-neopixel_blink.rs create mode 100644 examples/metro_m4-pwm.rs create mode 100644 examples/metro_m4-serial.rs create mode 100644 examples/metro_m4-spi.rs create mode 100644 examples/metro_m4-timer.rs create mode 100644 examples/metro_m4-trng.rs create mode 100644 examples/metro_m4-usb_logging.rs create mode 100644 examples/neo_trinkey-blinky_basic.rs create mode 100644 examples/neo_trinkey-blinky_rainbow.rs create mode 100644 examples/neo_trinkey-usb_ack.rs create mode 100644 examples/neokey_trinkey-blinky.rs create mode 100644 examples/neokey_trinkey-button.rs create mode 100644 examples/neokey_trinkey-rainbow.rs create mode 100644 examples/neokey_trinkey-usb_echo.rs create mode 100644 examples/p1am_100-adc.rs create mode 100644 examples/p1am_100-blinky_basic.rs create mode 100644 examples/p1am_100-clock.rs create mode 100644 examples/p1am_100-pwm.rs create mode 100644 examples/p1am_100-sleeping_timer.rs create mode 100644 examples/p1am_100-sleeping_timer_rtc.rs create mode 100644 examples/p1am_100-timers.rs create mode 100644 examples/p1am_100-uart_echo_rtic.rs create mode 100644 examples/p1am_100-usb_echo.rs create mode 100644 examples/p1am_100-usb_echo_rtic.rs create mode 100644 examples/pfza_proto1-blinky_basic.rs create mode 100644 examples/pygamer-blinky_basic.rs create mode 100644 examples/pygamer-blinky_rtic.rs create mode 100644 examples/pygamer-button_rtic.rs create mode 100644 examples/pygamer-clock_out.rs create mode 100644 examples/pygamer-ferris_img.rs create mode 100644 examples/pygamer-neopixel_adc_battery.rs create mode 100644 examples/pygamer-neopixel_adc_light.rs create mode 100644 examples/pygamer-neopixel_button.rs create mode 100644 examples/pygamer-neopixel_easing.rs create mode 100644 examples/pygamer-neopixel_rainbow.rs create mode 100644 examples/pygamer-pwm_tc4.rs create mode 100644 examples/pygamer-pwm_tcc0.rs create mode 100644 examples/pygamer-qspi.rs create mode 100644 examples/pygamer-sd_card.rs create mode 100644 examples/pygamer-timer.rs create mode 100644 examples/pygamer-usb_poll.rs create mode 100644 examples/pyportal-blinky_basic.rs create mode 100644 examples/pyportal-ili9341_240x320.rs create mode 100644 examples/pyportal-neopixel_rainbow.rs create mode 100644 examples/pyportal-usb_echo.rs create mode 100644 examples/qt_py_m0-neopixel.rs create mode 100644 examples/qt_py_m0-usb_echo.rs create mode 100644 examples/samd11_bare-adc.rs create mode 100644 examples/samd11_bare-blinky_basic.rs create mode 100644 examples/samd11_bare-blinky_rtic.rs create mode 100644 examples/samd11_bare-i2c.rs create mode 100644 examples/samd11_bare-pwm.rs create mode 100644 examples/samd11_bare-serial.rs create mode 100644 examples/samd11_bare-timer.rs create mode 100644 examples/samd21_mini-serial.rs create mode 100644 examples/serpente-blinky_basic.rs create mode 100644 examples/serpente-pwm.rs create mode 100644 examples/sodaq_one-blinky.rs create mode 100644 examples/sodaq_sara_aff-blinky_basic.rs create mode 100644 examples/trellis_m4-neopixel_accel.rs create mode 100644 examples/trellis_m4-neopixel_blink.rs create mode 100644 examples/trellis_m4-neopixel_keypad.rs create mode 100644 examples/trellis_m4-neopixel_orientation.rs create mode 100644 examples/trellis_m4-neopixel_rainbow.rs create mode 100644 examples/trinket_m0-blinky_basic.rs create mode 100644 examples/trinket_m0-dotstar.rs create mode 100644 examples/trinket_m0-eic.rs create mode 100644 examples/trinket_m0-pwm.rs create mode 100644 examples/trinket_m0-usb_serial.rs create mode 100644 examples/trinket_m0-watchdog.rs create mode 100644 examples/wio_lite_mg126-adc.rs create mode 100644 examples/wio_lite_mg126-adc_usb.rs create mode 100644 examples/wio_lite_mg126-blinky_basic.rs create mode 100644 examples/wio_lite_mg126-pwm.rs create mode 100644 examples/wio_lite_w600-blinky_basic.rs create mode 100644 examples/wio_lite_w600-usb_ack.rs create mode 100644 examples/wio_terminal-blinky.rs create mode 100644 examples/wio_terminal-buttons.rs create mode 100644 examples/wio_terminal-clock.rs create mode 100644 examples/wio_terminal-microphone.rs create mode 100644 examples/wio_terminal-orientation.rs create mode 100644 examples/wio_terminal-qspi.rs create mode 100644 examples/wio_terminal-sdcard.rs create mode 100644 examples/wio_terminal-snake.rs create mode 100644 examples/wio_terminal-usb_serial_display.rs create mode 100644 examples/wio_terminal-wifi_connect.rs create mode 100644 examples/wio_terminal-wifi_scan.rs create mode 100644 examples/xiao_m0-blink.rs create mode 100644 examples/xiao_m0-eic.rs create mode 100644 examples/xiao_m0-sercom_interrupt.rs create mode 100644 examples/xiao_m0-shared_i2c.rs create mode 100644 examples/xiao_m0-ssd1306_i2c.rs create mode 100644 examples/xiao_m0-usb_echo.rs diff --git a/examples/arduino_mkr1000-blinky_basic.rs b/examples/arduino_mkr1000-blinky_basic.rs new file mode 100644 index 000000000000..fe2700c5e7f3 --- /dev/null +++ b/examples/arduino_mkr1000-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use arduino_mkr1000 as bsp; +use bsp::hal; + +use bsp::{entry, pin_alias}; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[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::Pins::new(peripherals.PORT); + let mut led = pin_alias!(pins.led).into_push_pull_output(); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + delay.delay_ms(200u8); + led.set_low().unwrap(); + } +} diff --git a/examples/arduino_mkrvidor4000-blinky_basic.rs b/examples/arduino_mkrvidor4000-blinky_basic.rs new file mode 100644 index 000000000000..7c4c362b1e5e --- /dev/null +++ b/examples/arduino_mkrvidor4000-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +use arduino_mkrvidor4000 as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[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 mut pins = bsp::Pins::new(peripherals.PORT); + let mut led = pins.led_builtin.into_open_drain_output(&mut pins.port); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + delay.delay_ms(200u8); + led.set_low().unwrap(); + } +} diff --git a/examples/arduino_mkrvidor4000-enable_battery_charging.rs b/examples/arduino_mkrvidor4000-enable_battery_charging.rs new file mode 100644 index 000000000000..b4ac5db8a377 --- /dev/null +++ b/examples/arduino_mkrvidor4000-enable_battery_charging.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +use arduino_mkrvidor4000 as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::pad::PadPin; +use hal::prelude::*; +use hal::sercom::I2CMaster0; + +// https://www.ti.com/lit/ds/symlink/bq24195l.pdf +const PMIC_ADDRESS: u8 = 0x6B; +const PMIC_POWER_ON_CONFIGURATION_REGISTER: u8 = 0x01; +// Configure for Charge Battery + Minimum System Voltage Limit: 3.5V +const PMIC_POWER_ON_CONFIGURATION: u8 = 0b00011011; + +#[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 mut pins = bsp::Pins::new(peripherals.PORT); + let mut _led = pins.led_builtin.into_open_drain_output(&mut pins.port); + let gclk0 = clocks.gclk0(); + + let mut i2c: I2CMaster0< + hal::sercom::Sercom0Pad0>, + hal::sercom::Sercom0Pad1>, + > = I2CMaster0::new( + &clocks.sercom0_core(&gclk0).unwrap(), + 100.khz(), + peripherals.SERCOM0, + &mut peripherals.PM, + // Arduino MKR Vidor 4000 has I2C on pins PA08, PA09 + pins.sda.into_pad(&mut pins.port), + pins.scl.into_pad(&mut pins.port), + ); + + i2c.write( + PMIC_ADDRESS, + &[ + PMIC_POWER_ON_CONFIGURATION_REGISTER, + PMIC_POWER_ON_CONFIGURATION, + ], + ) + .unwrap(); + loop {} +} diff --git a/examples/arduino_mkrvidor4000-run_fpga.rs b/examples/arduino_mkrvidor4000-run_fpga.rs new file mode 100644 index 000000000000..c64a15ca7e8b --- /dev/null +++ b/examples/arduino_mkrvidor4000-run_fpga.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +use arduino_mkrvidor4000 as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{CorePeripherals, Peripherals}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let _corecore = CorePeripherals::take().unwrap(); + let _clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = bsp::Pins::new(peripherals.PORT); + + // Enable 48MHZ clock output for FPGA + // https://github.com/arduino/ArduinoCore-samd/blob/master/variants/mkrvidor4000/variant.cpp#L229 + let _fpga_clk = pins.gclk.into_function_h(&mut pins.port); + + loop {} +} diff --git a/examples/arduino_mkrzero-blinky_basic.rs b/examples/arduino_mkrzero-blinky_basic.rs new file mode 100644 index 000000000000..f74bec8dcddd --- /dev/null +++ b/examples/arduino_mkrzero-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +use arduino_mkrzero as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[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::Pins::new(peripherals.PORT); + let mut led = bsp::pin_alias!(pins.led).into_push_pull_output(); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + delay.delay_ms(200u8); + led.set_low().unwrap(); + } +} diff --git a/examples/arduino_mkrzero-blinky_rtic.rs b/examples/arduino_mkrzero-blinky_rtic.rs new file mode 100644 index 000000000000..73edc4d9ae79 --- /dev/null +++ b/examples/arduino_mkrzero-blinky_rtic.rs @@ -0,0 +1,73 @@ +//! Uses RTIC with the RTC as time source to blink an LED. +//! +//! The idle task is sleeping the CPU, so in practice this gives similar power +//! figure as the "sleeping_timer_rtc" example. +#![no_std] +#![no_main] + +use arduino_mkrzero as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; +use rtic::app; + +#[app(device = bsp::pac, peripherals = true, dispatchers = [EVSYS])] +mod app { + use super::*; + use bsp::hal; + use hal::clock::{ClockGenId, ClockSource, GenericClockController}; + use hal::pac::Peripherals; + use hal::prelude::*; + use hal::rtc::{Count32Mode, Duration, Rtc}; + + #[local] + struct Local {} + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + led: bsp::pins::Led, + } + + #[monotonic(binds = RTC, default = true)] + type RtcMonotonic = Rtc; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let mut peripherals: Peripherals = cx.device; + let pins = bsp::pins::Pins::new(peripherals.PORT); + let mut core: rtic::export::Peripherals = cx.core; + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + let _gclk = clocks.gclk0(); + let rtc_clock_src = clocks + .configure_gclk_divider_and_source(ClockGenId::GCLK2, 1, ClockSource::XOSC32K, false) + .unwrap(); + clocks.configure_standby(ClockGenId::GCLK2, true); + let rtc_clock = clocks.rtc(&rtc_clock_src).unwrap(); + let rtc = Rtc::count32_mode(peripherals.RTC, rtc_clock.freq(), &mut peripherals.PM); + let led = bsp::pin_alias!(pins.led).into(); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + // Start the blink task + blink::spawn().unwrap(); + + (Shared { led }, Local {}, init::Monotonics(rtc)) + } + + #[task(shared = [led])] + fn blink(mut cx: blink::Context) { + // If the LED were a local resource, the lock would not be necessary + let _ = cx.shared.led.lock(|led| led.toggle()); + blink::spawn_after(Duration::secs(3)).ok(); + } +} diff --git a/examples/arduino_mkrzero-pwm.rs b/examples/arduino_mkrzero-pwm.rs new file mode 100644 index 000000000000..d98635981459 --- /dev/null +++ b/examples/arduino_mkrzero-pwm.rs @@ -0,0 +1,54 @@ +#![no_std] +#![no_main] + +use arduino_mkrzero as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::{GenericClockController, Tcc0Tcc1Clock}; +use hal::delay::Delay; +use hal::gpio::AlternateE; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::pwm::{Channel, Pwm0}; + +#[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 mut delay = Delay::new(core.SYST, &mut clocks); + let pins = bsp::pins::Pins::new(peripherals.PORT); + + // PWM0_CH1 is A4 on the board - pin 19 or PA05 + // see: https://github.com/arduino/ArduinoCore-samd/blob/master/variants/mkrzero/variant.cpp + let _a4 = pins.pa04.into_mode::(); + let gclk0 = clocks.gclk0(); + + let tcc0_tcc1_clock: &Tcc0Tcc1Clock = &clocks.tcc0_tcc1(&gclk0).unwrap(); + + let mut pwm0 = Pwm0::new( + &tcc0_tcc1_clock, + 1u32.kHz(), + peripherals.TCC0, + &mut peripherals.PM, + ); + let max_duty = pwm0.get_max_duty(); + pwm0.enable(Channel::_1); + loop { + pwm0.set_duty(Channel::_1, max_duty); + delay.delay_ms(500u16); + pwm0.set_duty(Channel::_1, max_duty / 4); + delay.delay_ms(500u16); + } +} diff --git a/examples/arduino_mkrzero-usb_logging.rs b/examples/arduino_mkrzero-usb_logging.rs new file mode 100644 index 000000000000..2a777ae801eb --- /dev/null +++ b/examples/arduino_mkrzero-usb_logging.rs @@ -0,0 +1,105 @@ +#![no_std] +#![no_main] + +use arduino_mkrzero as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::prelude::*; + +use hal::usb::UsbBus; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::peripheral::NVIC; + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut 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::Pins::new(peripherals.PORT); + let mut led = bsp::pin_alias!(pins.led).into_push_pull_output(); + let mut delay = Delay::new(core.SYST, &mut clocks); + + let usb_n = bsp::pin_alias!(pins.usb_n); + let usb_p = bsp::pin_alias!(pins.usb_p); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb::usb_allocator( + peripherals.USB, + &mut clocks, + &mut peripherals.PM, + usb_n.into(), + usb_p.into(), + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x2222, 0x3333)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + delay.delay_ms(200u8); + led.set_low().unwrap(); + + // Turn off interrupts so we don't fight with the interrupt + cortex_m::interrupt::free(|_| unsafe { + if let Some(serial) = USB_SERIAL.as_mut() { + let _ = serial.write("log line\r\n".as_bytes()); + } + }); + } +} + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + // Make the other side happy + let mut buf = [0u8; 16]; + let _ = serial.read(&mut buf); + } + } + }; +} + +#[interrupt] +fn USB() { + poll_usb(); +} diff --git a/examples/arduino_nano33iot-blinky_basic.rs b/examples/arduino_nano33iot-blinky_basic.rs new file mode 100644 index 000000000000..665d6793c6ed --- /dev/null +++ b/examples/arduino_nano33iot-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +use arduino_nano33iot as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut led: bsp::Led = pins.led_sck.into(); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + delay.delay_ms(200u8); + led.set_low().unwrap(); + } +} diff --git a/examples/arduino_nano33iot-i2c_ssd1306.rs b/examples/arduino_nano33iot-i2c_ssd1306.rs new file mode 100644 index 000000000000..a84e40136961 --- /dev/null +++ b/examples/arduino_nano33iot-i2c_ssd1306.rs @@ -0,0 +1,85 @@ +//! +//! Based on an example from https://github.com/jamwaffles/ssd1306 +//! Sends random raw data to the display, emulating an old untuned TV. +//! Retrieves the underlying display properties struct and allows calling of the +//! low-level `draw()` method, sending a 1024 byte buffer straight to the +//! display. +//! +//! Uses SmallRng as random number generator. +//! NOTE: these are pseudorandom numbers, not suitable for cryptographic or +//! similar purposes. + +#![no_std] +#![no_main] + +use arduino_nano33iot as bsp; +use bsp::hal; + +use rand; +use ssd1306; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::time::KiloHertz; + +use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; + +use rand::prelude::*; + +const BOOT_DELAY_MS: u16 = 100; //small delay for the I2C to initiate correctly and start on boot without + // having to reset the board + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut delay = Delay::new(core.SYST, &mut clocks); + + delay.delay_ms(BOOT_DELAY_MS); + + let i2c = bsp::i2c_master( + &mut clocks, + KiloHertz(400), + peripherals.SERCOM4, + &mut peripherals.PM, + pins.sda, + pins.scl, + ); + + let interface = I2CDisplayInterface::new(i2c); + let mut disp = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) + .into_buffered_graphics_mode(); + disp.init().unwrap(); + + let mut buf = [0x00u8; 512]; //each line has 128 pixels, that's 8 bytes, x 32 lines = 512 bytes for the + // whole display + + let mut rng = SmallRng::seed_from_u64(0x0101_0303_0808_0909); + + loop { + rng.fill_bytes(&mut buf); // repeatedly fills the display buffer with randomly generated bytes + disp.bounded_draw( + &buf, + DisplaySize128x64::WIDTH.into(), + (0, 0), + (DisplaySize128x64::WIDTH, DisplaySize128x64::HEIGHT), + ) + .unwrap(); + disp.flush().unwrap(); + } +} diff --git a/examples/arduino_nano33iot-serial.rs b/examples/arduino_nano33iot-serial.rs new file mode 100644 index 000000000000..aa7a264d9da8 --- /dev/null +++ b/examples/arduino_nano33iot-serial.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] + +use arduino_nano33iot as bsp; +use bsp::hal; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut delay = Delay::new(core.SYST, &mut clocks); + + let mut uart = bsp::uart( + &mut clocks, + 9600.hz(), + peripherals.SERCOM5, + &mut peripherals.PM, + pins.rx, + pins.tx, + ); + + loop { + // print ASCII characters from ! to ~ + for ch in 33..127 { + uart.write(ch).unwrap(); + delay.delay_ms(500u16); + } + } +} diff --git a/examples/arduino_nano33iot-spi_st7735.rs b/examples/arduino_nano33iot-spi_st7735.rs new file mode 100644 index 000000000000..a867ae5ae116 --- /dev/null +++ b/examples/arduino_nano33iot-spi_st7735.rs @@ -0,0 +1,82 @@ +#![no_std] +#![no_main] + +use arduino_nano33iot as bsp; +use bsp::hal; + +use embedded_graphics; +use st7735_lcd; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::time::MegaHertz; + +use embedded_graphics::{ + image::{Image, ImageRaw, ImageRawLE}, + pixelcolor::Rgb565, + prelude::*, + primitives::{PrimitiveStyleBuilder, Rectangle}, +}; + +use st7735_lcd::Orientation; + +const BOOT_DELAY_MS: u16 = 100; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut delay = Delay::new(core.SYST, &mut clocks); + + delay.delay_ms(BOOT_DELAY_MS); + + let spi = bsp::spi_master( + &mut clocks, + MegaHertz(16), + peripherals.SERCOM1, + &mut peripherals.PM, + pins.led_sck, + pins.mosi, + pins.miso, + ); + + let dc = pins.d6.into_push_pull_output(); + let rst = pins.d9.into_push_pull_output(); + + let mut disp = st7735_lcd::ST7735::new(spi, dc, rst, true, false, 160, 128); + + disp.init(&mut delay).unwrap(); + disp.set_orientation(&Orientation::Landscape).unwrap(); + let style = PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLACK) + .build(); + + Rectangle::with_corners(Point::new(0, 0), Point::new(160, 128)) + .into_styled(style) + .draw(&mut disp) + .unwrap(); + + disp.set_offset(0, 25); + + // draw ferris + let image_raw: ImageRawLE = ImageRaw::new(include_bytes!("assets/ferris.raw"), 86); + let image = Image::new(&image_raw, Point::new(34, 8)); + image.draw(&mut disp).unwrap(); + + loop {} +} diff --git a/examples/arduino_nano33iot-usb_logging.rs b/examples/arduino_nano33iot-usb_logging.rs new file mode 100644 index 000000000000..4c0d32394c88 --- /dev/null +++ b/examples/arduino_nano33iot-usb_logging.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] + +use arduino_nano33iot as bsp; +use bsp::hal; +use bsp::hal::prelude::*; + +use usb_device; +use usbd_serial; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; + +use hal::usb::UsbBus; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; + +#[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 mut led: bsp::Led = pins.led_sck.into(); + + 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(0x2222, 0x3333)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + // Flash the LED in a spin loop to demonstrate that USB is + // entirely interrupt driven. + loop { + cycle_delay(5 * 1024 * 1024); + led.toggle().unwrap(); + + // Turn off interrupts so we don't fight with the interrupt + cortex_m::interrupt::free(|_| unsafe { + USB_BUS.as_mut().map(|_| { + USB_SERIAL.as_mut().map(|serial| { + // Skip errors so we can continue the program + let _ = serial.write("log line\r\n".as_bytes()); + }); + }) + }); + } +} + +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]); + + // Make the other side happy + let mut buf = [0u8; 16]; + let _ = serial.read(&mut buf); + }); + }); + }; +} + +#[interrupt] +fn USB() { + poll_usb(); +} diff --git a/examples/atsame54_xpro-blinky_basic.rs b/examples/atsame54_xpro-blinky_basic.rs new file mode 100644 index 000000000000..d18af5f4adb0 --- /dev/null +++ b/examples/atsame54_xpro-blinky_basic.rs @@ -0,0 +1,42 @@ +#![no_std] +#![no_main] + +use atsame54_xpro as bsp; +use bsp::hal; + +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; + +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[cortex_m_rt::entry] +fn main() -> ! { + rtt_init_print!(); + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + let pins = bsp::Pins::new(peripherals.port); + let mut led = bsp::pin_alias!(pins.led).into_push_pull_output(); + + loop { + delay.delay_ms(200u8); + led.set_high().unwrap(); + rprintln!("LED OFF"); + delay.delay_ms(200u8); + led.set_low().unwrap(); + rprintln!("LED ON"); + } +} diff --git a/examples/atsame54_xpro-blinky_rtic.rs b/examples/atsame54_xpro-blinky_rtic.rs new file mode 100644 index 000000000000..e331bedfcfa9 --- /dev/null +++ b/examples/atsame54_xpro-blinky_rtic.rs @@ -0,0 +1,83 @@ +#![no_std] +#![no_main] + +use atsame54_xpro as bsp; +use bsp::hal; +use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::prelude::*; +use hal::rtc::rtic::rtc_clock; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +// TODO: Any reason this cannot be in a HAL's prelude? +use hal::ehal::digital::StatefulOutputPin; + +hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); + +#[rtic::app(device = hal::pac, dispatchers = [FREQM])] +mod app { + use super::*; + + #[shared] + struct Shared {} + + #[local] + struct Local { + led: bsp::Led, + } + + #[init] + fn init(ctx: init::Context) -> (Shared, Local) { + let mut device = ctx.device; + let mut core: rtic::export::Peripherals = ctx.core; + + rtt_init_print!(); + + let (_buses, clocks, tokens) = clock_system_at_reset( + device.oscctrl, + device.osc32kctrl, + device.gclk, + device.mclk, + &mut device.nvmctrl, + ); + + // Enable the 1 kHz clock from the internal 32 kHz source + let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); + + // Enable the RTC clock with the 1 kHz source. + // Note that currently the proof of this (the `RtcOsc` instance) is not + // required to start the monotonic. + let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + + // Start the monotonic + Mono::start(device.rtc); + + let pins = bsp::Pins::new(device.port); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + blink_led::spawn().unwrap(); + + ( + Shared {}, + Local { + led: bsp::pin_alias!(pins.led).into(), + }, + ) + } + + /// This function is spawned and never returns. + #[task(priority = 1, local=[led])] + async fn blink_led(ctx: blink_led::Context) { + StatefulOutputPin::toggle(ctx.local.led).unwrap(); + rprintln!( + "LED {}!", + if ctx.local.led.is_set_high().unwrap() { + "OFF" + } else { + "ON" + } + ); + Mono::delay(200u64.millis()).await; + } +} diff --git a/examples/atsame54_xpro-mcan.rs b/examples/atsame54_xpro-mcan.rs new file mode 100644 index 000000000000..5cad635297f6 --- /dev/null +++ b/examples/atsame54_xpro-mcan.rs @@ -0,0 +1,319 @@ +//! MCAN example +//! Assumed bus: +//! - 375 kb/s nominal bitrate +//! - 750 kb/s data bitrate if sending/receiving CAN FD frames with bitrate +//! switching +//! +//! 1. Sends a message over CAN on SW0 button press and prints out transmit +//! event queue content, protocol status register and error counter register +//! in RTT terminal. +//! +//! 2. Sets up an interrupt line and message filters: +//! +//! - messages with standard IDs will end up in RxFifo0 +//! - messages with extended IDs will end up in RxFifo1 +//! - messages content will be printed out in RTT terminal upon arrival +//! +//! 3. LED0 will blink to indicate activity (sending & receiving) + +#![no_std] +#![no_main] + +use atsame54_xpro as bsp; +use bsp::hal; +use clock::{osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::clock::v2 as clock; +use hal::eic::{Ch15, Eic, ExtInt, Sense}; +use hal::gpio::{Interrupt as GpioInterrupt, *}; +use hal::prelude::*; +use hal::rtc::rtic::rtc_clock; + +use mcan::embedded_can as ecan; +use mcan::generic_array::typenum::consts::*; +use mcan::interrupt::{Interrupt, InterruptLine, OwnedInterruptSet}; +use mcan::message::rx; +use mcan::message::tx; +use mcan::messageram::SharedMemory; +use mcan::prelude::*; +use mcan::rx_fifo::Fifo0; +use mcan::rx_fifo::Fifo1; +use mcan::rx_fifo::RxFifo; +use mcan::{ + config::{BitTiming, Mode}, + filter::{Action, ExtFilter, Filter}, +}; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; + +hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); + +pub struct Capacities; + +impl mcan::messageram::Capacities for Capacities { + type StandardFilters = U1; + type ExtendedFilters = U1; + type RxBufferMessage = rx::Message<64>; + type DedicatedRxBuffers = U0; + type RxFifo0Message = rx::Message<64>; + type RxFifo0 = U64; + type RxFifo1Message = rx::Message<64>; + type RxFifo1 = U64; + type TxMessage = tx::Message<64>; + type TxBuffers = U32; + type DedicatedTxBuffers = U0; + type TxEventFifo = U32; +} + +type RxFifo0 = RxFifo< + 'static, + Fifo0, + clock::types::Can1, + ::RxFifo0Message, +>; +type RxFifo1 = RxFifo< + 'static, + Fifo1, + clock::types::Can1, + ::RxFifo1Message, +>; +type Tx = mcan::tx_buffers::Tx<'static, clock::types::Can1, Capacities>; +type TxEventFifo = mcan::tx_event_fifo::TxEventFifo<'static, clock::types::Can1>; +type Aux = mcan::bus::Aux< + 'static, + clock::types::Can1, + hal::can::Dependencies< + clock::types::Can1, + clock::gclk::Gclk0Id, + bsp::Ata6561Rx, + bsp::Ata6561Tx, + bsp::pac::Can1, + >, +>; +type Button = ExtInt>, Ch15>; + +#[rtic::app(device = hal::pac, dispatchers = [FREQM])] +mod app { + use super::*; + + #[shared] + struct Shared {} + + #[local] + struct Local { + button: Button, + led: bsp::Led, + line_interrupts: OwnedInterruptSet, + rx_fifo_0: RxFifo0, + rx_fifo_1: RxFifo1, + tx: Tx, + tx_event_fifo: TxEventFifo, + aux: Aux, + } + + #[init(local = [ + #[link_section = ".can"] + can_memory: SharedMemory = SharedMemory::new() + ])] + fn init(ctx: init::Context) -> (Shared, Local) { + let mut device = ctx.device; + + rtt_init_print!(); + rprintln!("Application up!"); + + let (_buses, clocks, tokens) = clock::clock_system_at_reset( + device.oscctrl, + device.osc32kctrl, + device.gclk, + device.mclk, + &mut device.nvmctrl, + ); + + // Enable the 1 kHz clock from the internal 32 kHz source + let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); + + // Enable the RTC clock with the 1 kHz source. + // Note that currently the proof of this (the `RtcOsc` instance) is not + // required to start the monotonic. + let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + + // Start the monotonic + Mono::start(device.rtc); + + // Need to get the MCLK peripheral back due to things in the HAL still using v1 + // of the clocks API + let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() }; + + let pins = bsp::Pins::new(device.port); + + let (pclk_eic, gclk0) = clock::pclk::Pclk::enable(tokens.pclks.eic, clocks.gclk0); + + let eic_channels = Eic::new(&mut mclk, &pclk_eic.into(), device.eic).split(); + let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei(eic_channels.15); + button.sense(Sense::Fall); + button.debounce(); + button.enable_interrupt(); + + let can1_rx = bsp::pin_alias!(pins.ata6561_rx).into_mode(); + let can1_tx = bsp::pin_alias!(pins.ata6561_tx).into_mode(); + let mut can1_standby = bsp::pin_alias!(pins.ata6561_standby).into_push_pull_output(); + + let _ = can1_standby.set_low(); + + let (pclk_can1, gclk0) = clock::pclk::Pclk::enable(tokens.pclks.can1, gclk0); + + let (dependencies, _gclk0) = hal::can::Dependencies::new( + gclk0, + pclk_can1, + clocks.ahbs.can1, + can1_rx, + can1_tx, + device.can1, + ); + + let mut can = + mcan::bus::CanConfigurable::new(375.kHz(), dependencies, ctx.local.can_memory).unwrap(); + + can.config().mode = Mode::Fd { + allow_bit_rate_switching: true, + data_phase_timing: BitTiming::new(750.kHz()), + }; + + let line_interrupts = can + .interrupts() + .enable( + [ + Interrupt::RxFifo0NewMessage, + Interrupt::RxFifo0Full, + Interrupt::RxFifo0MessageLost, + Interrupt::RxFifo1NewMessage, + Interrupt::RxFifo1Full, + Interrupt::RxFifo1MessageLost, + ] + .into_iter() + .collect(), + InterruptLine::Line0, + // ATSAMD chips do not expose separate NVIC lines to MCAN + // InterruptLine::Line0 and InterruptLine::Line1 are wired + // together in the hardware. + ) + .unwrap(); + + can.filters_standard() + .push(Filter::Classic { + action: Action::StoreFifo0, + filter: ecan::StandardId::MAX, + mask: ecan::StandardId::ZERO, + }) + .unwrap_or_else(|_| panic!("Standard filter application failed")); + + can.filters_extended() + .push(ExtFilter::Classic { + action: Action::StoreFifo1, + filter: ecan::ExtendedId::MAX, + mask: ecan::ExtendedId::ZERO, + }) + .unwrap_or_else(|_| panic!("Extended filter application failed")); + + let can = can.finalize().unwrap(); + + let rx_fifo_0 = can.rx_fifo_0; + let rx_fifo_1 = can.rx_fifo_1; + let tx = can.tx; + let tx_event_fifo = can.tx_event_fifo; + let aux = can.aux; + + let led = bsp::pin_alias!(pins.led).into(); + + let _ = activity_led::spawn(true); + + ( + Shared {}, + Local { + button, + led, + line_interrupts, + rx_fifo_0, + rx_fifo_1, + tx, + tx_event_fifo, + aux, + }, + ) + } + + #[task(binds = EIC_EXTINT_15, local = [counter: u16 = 0, button, tx_event_fifo, aux, tx])] + fn button(ctx: button::Context) { + ctx.local.button.clear_interrupt(); + let _ = activity_led::spawn(true); + rprintln!("Button pressed! Status:"); + while let Some(e) = ctx.local.tx_event_fifo.pop() { + rprintln!("TxEvent: {:0X?}", e); + } + rprintln!("{:?}", ctx.local.aux.protocol_status()); + rprintln!("{:?}", ctx.local.aux.error_counters()); + + let counter = *ctx.local.counter; + let wrapped_counter = (counter % u8::MAX as u16) as u8; + let mut payload = [0_u8; 64]; + payload.fill(wrapped_counter); + + ctx.local + .tx + .transmit_queued( + tx::MessageBuilder { + id: ecan::Id::Extended(ecan::ExtendedId::new(counter as _).unwrap()), + frame_type: tx::FrameType::FlexibleDatarate { + payload: &payload, + bit_rate_switching: true, + force_error_state_indicator: false, + }, + store_tx_event: Some(wrapped_counter), + } + .build() + .unwrap(), + ) + .unwrap(); + rprintln!("Message {:0X} sent!", counter); + *ctx.local.counter += 1; + } + + #[task(priority = 2, binds = CAN1, local = [line_interrupts, rx_fifo_0, rx_fifo_1])] + fn can1(mut ctx: can1::Context) { + let _ = activity_led::spawn(true); + let line_interrupts = ctx.local.line_interrupts; + for interrupt in line_interrupts.iter_flagged() { + match interrupt { + Interrupt::RxFifo0NewMessage => { + for message in &mut ctx.local.rx_fifo_0 { + log("RxFifo0", &message); + } + } + Interrupt::RxFifo1NewMessage => { + for message in &mut ctx.local.rx_fifo_1 { + log("RxFifo1", &message); + } + } + i => rprintln!("{:?} interrupt triggered", i), + } + } + } + + /// This function is spawned and never returns. + #[task(priority = 1, local = [led])] + async fn activity_led(ctx: activity_led::Context, led_on: bool) { + let _ = ctx.local.led.set_state((!led_on).into()); + if led_on { + Mono::delay(100u64.millis()).await; + } + } + + fn log(fifo: &str, message: &impl mcan::message::Raw) { + rprintln!("New message received ({})", fifo); + rprintln!("id: {:0X?}", message.id()); + rprintln!("decoded_dlc: {:?}", message.decoded_dlc()); + rprintln!("fd_format: {:?}", message.fd_format()); + rprintln!("bit_rate_switching: {:?}", message.bit_rate_switching()); + rprintln!("is_remote_frame: {:?}", message.is_remote_frame()); + rprintln!("data: {:0X?}", message.data()); + } +} diff --git a/examples/circuit_playground_express-blinky_basic.rs b/examples/circuit_playground_express-blinky_basic.rs new file mode 100644 index 000000000000..c3387886de5c --- /dev/null +++ b/examples/circuit_playground_express-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::hal; +use bsp::pac; +use circuit_playground_express as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::prelude::*; +use pac::{CorePeripherals, Peripherals}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut red_led: bsp::RedLed = pins.d13.into(); + let mut delay = Delay::new(core.SYST, &mut clocks); + loop { + delay.delay_ms(200u8); + red_led.set_high().unwrap(); + delay.delay_ms(200u8); + red_led.set_low().unwrap(); + } +} diff --git a/examples/circuit_playground_express-neopixel_rainbow.rs b/examples/circuit_playground_express-neopixel_rainbow.rs new file mode 100644 index 000000000000..754221562499 --- /dev/null +++ b/examples/circuit_playground_express-neopixel_rainbow.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +// Neopixel Rainbow +// This only functions when the --release version is compiled. Using the debug +// version leads to slow pulse durations which results in a straight white LED +// output. +// +// // Needs to be compiled with --release for the timing to be correct + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::hal; +use bsp::pac; +use circuit_playground_express as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::prelude::*; +use hal::timer::TimerCounter; +use pac::{CorePeripherals, Peripherals}; + +use smart_leds::{ + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; +use ws2812_timer_delay as ws2812; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let 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 mut delay = Delay::new(core.SYST, &mut clocks); + + 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 neopixel_pin: bsp::NeoPixel = pins.d8.into(); + let mut neopixel = ws2812::Ws2812::new(timer, neopixel_pin); + + // Loop through all of the available hue values (colors) to make a + // rainbow effect from the onboard neopixel + loop { + for j in 0..255u8 { + let colors = [hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 2, + }); 10]; + neopixel.write(colors.iter().cloned()).unwrap(); + delay.delay_ms(5u8); + } + } +} diff --git a/examples/circuit_playground_express-uart.rs b/examples/circuit_playground_express-uart.rs new file mode 100644 index 000000000000..9ca526a645ab --- /dev/null +++ b/examples/circuit_playground_express-uart.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::hal; +use bsp::pac; +use circuit_playground_express as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::prelude::*; + +use pac::{CorePeripherals, Peripherals}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.PM, + &mut peripherals.SYSCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut pm = peripherals.PM; + let pins = bsp::Pins::new(peripherals.PORT); + let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); + + let mut red_led = pins.d13.into_push_pull_output(); + + // Setup UART peripheral. + let (rx_pin, tx_pin) = (pins.a6, pins.a7); + let mut uart = bsp::uart( + &mut clocks, + 9600.hz(), + peripherals.SERCOM4, + &mut pm, + rx_pin, + tx_pin, + ); + + // Write out a message on start up. + for byte in b"Hello world!\r\n" { + nb::block!(uart.write(*byte)).unwrap(); + } + + loop { + match uart.read() { + Ok(byte) => { + // Echo all received characters. + nb::block!(uart.write(byte)).unwrap(); + + // Blink the red led to show that a character has arrived. + red_led.set_high().unwrap(); + delay.delay_ms(2u16); + red_led.set_low().unwrap(); + } + Err(_) => delay.delay_ms(5u16), + }; + } +} diff --git a/examples/circuit_playground_express-usb_serial.rs b/examples/circuit_playground_express-usb_serial.rs new file mode 100644 index 000000000000..b99fbcd7c43e --- /dev/null +++ b/examples/circuit_playground_express-usb_serial.rs @@ -0,0 +1,103 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting 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 bsp::hal; +use bsp::pac; +use circuit_playground_express as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::prelude::*; +use hal::usb::UsbBus; +use pac::{interrupt, CorePeripherals, Peripherals}; + +#[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 mut red_led: bsp::RedLed = pins.d13.into(); + + 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("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + // Flash the LED in a spin loop to demonstrate that USB is + // entirely interrupt driven. + loop { + cycle_delay(15 * 1024 * 1024); + red_led.set_high().ok(); + cycle_delay(15 * 1024 * 1024); + red_led.set_low().ok(); + } +} + +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(&[c.clone()]).ok(); + } + }; + }); + }); + }; +} + +#[interrupt] +fn USB() { + poll_usb(); +} diff --git a/examples/edgebadge-blinky_basic.rs b/examples/edgebadge-blinky_basic.rs new file mode 100644 index 000000000000..385f16ad5b16 --- /dev/null +++ b/examples/edgebadge-blinky_basic.rs @@ -0,0 +1,43 @@ +//! Blink an led without using the BSP split() method. + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::prelude::*; +use hal::watchdog::{Watchdog, WatchdogTimeout}; +use pac::{CorePeripherals, Peripherals}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut delay = Delay::new(core.SYST, &mut clocks); + delay.delay_ms(400u16); + + let mut pins = Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_open_drain_output(&mut pins.port); + + let mut wdt = Watchdog::new(peripherals.WDT); + wdt.start(WatchdogTimeout::Cycles256 as u8); + + loop { + delay.delay_ms(200u8); + wdt.feed(); + red_led.set_high().unwrap(); + delay.delay_ms(200u8); + wdt.feed(); + red_led.set_low().unwrap(); + } +} diff --git a/examples/edgebadge-button_rtic.rs b/examples/edgebadge-button_rtic.rs new file mode 100644 index 000000000000..82c4f9aaf5ae --- /dev/null +++ b/examples/edgebadge-button_rtic.rs @@ -0,0 +1,79 @@ +#![no_std] +#![no_main] + +use panic_halt as _; + +use bsp::{pins::ButtonReader, pins::Keys, Pins}; +use edgebadge as bsp; + +#[rtic::app(device = bsp::pac, peripherals = true)] +mod app { + use super::*; + + use bsp::clock::GenericClockController; + use bsp::gpio::{OpenDrain, Output, Pa23}; + use bsp::prelude::*; + + #[local] + struct Local { + red_led: Pa23>, + timer: bsp::timer::TimerCounter3, + buttons: ButtonReader, + } + + #[shared] + struct Shared {} + + /// This function is called each time the tc3 interrupt triggers. + /// We use it to toggle the LED. The `wait()` call is important + /// because it checks and resets the counter ready for the next + /// period. + #[task(binds = TC3, local = [timer, red_led, buttons])] + fn tc3(c: tc3::Context) { + if c.local.timer.wait().is_ok() { + for event in c.local.buttons.events() { + match event { + Keys::SelectDown => { + c.local.red_led.set_high().ok(); + } + Keys::SelectUp => { + c.local.red_led.set_low().ok(); + } + _ => {} + } + } + } + } + + #[init] + fn init(c: init::Context) -> (Shared, Local, init::Monotonics) { + let mut device = c.device; + let mut clocks = GenericClockController::with_internal_32kosc( + device.GCLK, + &mut device.MCLK, + &mut device.OSC32KCTRL, + &mut device.OSCCTRL, + &mut device.NVMCTRL, + ); + + let mut pins = Pins::new(device.PORT).split(); + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tc2_tc3(&gclk0).unwrap(); + + let mut tc3 = bsp::timer::TimerCounter::tc3_(&timer_clock, device.TC3, &mut device.MCLK); + + tc3.start(200.hz()); + tc3.enable_interrupt(); + + ( + Shared {}, + Local { + buttons: pins.buttons.init(&mut pins.port), + red_led: pins.led_pin.into_open_drain_output(&mut pins.port), + timer: tc3, + }, + init::Monotonics(), + ) + } +} diff --git a/examples/edgebadge-ferris_img.rs b/examples/edgebadge-ferris_img.rs new file mode 100644 index 000000000000..c65132cd3edf --- /dev/null +++ b/examples/edgebadge-ferris_img.rs @@ -0,0 +1,63 @@ +//! Place a bitmap image on the screen. Convert png to .bmp +//! * Resize and export images directly from image editor by saving as .bmp and +//! choosing 16bit R5 G6 B5 +//! * OR Convert with imagemagick: convert rustacean-flat-noshadow.png -type +//! truecolor -define bmp:subtype=RGB565 -depth 16 -strip -resize 86x64 +//! ferris.bmp + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use embedded_graphics::image::Image; +use embedded_graphics::pixelcolor::{Rgb565, RgbColor}; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; +use hal::clock::GenericClockController; +use pac::{CorePeripherals, Peripherals}; +use tinybmp::Bmp; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = Pins::new(peripherals.PORT).split(); + let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); + + let (mut display, _backlight) = pins + .display + .init( + &mut clocks, + peripherals.SERCOM4, + &mut peripherals.MCLK, + peripherals.TC2, + &mut delay, + &mut pins.port, + ) + .unwrap(); + + // black out the screen + Rectangle::with_corners(Point::new(0, 0), Point::new(160, 128)) + .into_styled( + PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLACK) + .build(), + ) + .draw(&mut display) + .unwrap(); + + let raw_image: Bmp = Bmp::from_slice(include_bytes!("../assets/ferris.bmp")).unwrap(); + let ferris = Image::new(&raw_image, Point::new(32, 32)); + + ferris.draw(&mut display).unwrap(); + loop {} +} diff --git a/examples/edgebadge-neopixel_adc_battery.rs b/examples/edgebadge-neopixel_adc_battery.rs new file mode 100644 index 000000000000..34ff69e6126d --- /dev/null +++ b/examples/edgebadge-neopixel_adc_battery.rs @@ -0,0 +1,82 @@ +//! Display battery percentage on the neopixels. +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::adc::Adc; +use hal::prelude::*; +use hal::timer::SpinTimer; +use hal::{clock::GenericClockController, delay::Delay}; +use pac::gclk::pchctrl::GEN_A::GCLK11; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::{brightness, hsv::RGB8, SmartLedsWrite}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = Pins::new(peripherals.PORT).split(); + + let mut adc0 = Adc::adc0(peripherals.ADC0, &mut peripherals.MCLK, &mut clocks, GCLK11); + let mut battery = pins.battery.init(&mut pins.port); + + // neopixels + let timer = SpinTimer::new(4); + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + //todo put this on a .. 10minute, 30min, update timer + loop { + let battery_data = battery.read(&mut adc0); + + let mut colors = [ + RGB8::default(), + RGB8::default(), + RGB8::default(), + RGB8::default(), + RGB8::default(), + ]; + + if battery_data < 3.6 { + colors[0] = RGB8::from((255, 0, 0)); + } else if (battery_data >= 3.6) && (battery_data < 3.8) { + colors[0] = RGB8::from((255, 0, 0)); + colors[1] = RGB8::from((255, 0, 0)); + } else if (battery_data >= 3.8) && (battery_data < 3.9) { + colors[0] = RGB8::from((255, 255, 0)); + colors[1] = RGB8::from((255, 255, 0)); + colors[2] = RGB8::from((255, 255, 0)); + } else if (battery_data >= 3.9) && (battery_data < 4.0) { + colors[0] = RGB8::from((0, 255, 0)); + colors[1] = RGB8::from((0, 255, 0)); + colors[2] = RGB8::from((0, 255, 0)); + colors[3] = RGB8::from((0, 255, 0)); + } else { + colors[0] = RGB8::from((0, 255, 0)); + colors[1] = RGB8::from((0, 255, 0)); + colors[2] = RGB8::from((0, 255, 0)); + colors[3] = RGB8::from((0, 255, 0)); + colors[4] = RGB8::from((0, 255, 0)); + }; + + neopixel + .write(brightness(colors.iter().cloned(), 1)) + .unwrap(); + + delay.delay_ms(10u8); + } +} diff --git a/examples/edgebadge-neopixel_adc_light.rs b/examples/edgebadge-neopixel_adc_light.rs new file mode 100644 index 000000000000..7c0ff5e05c5e --- /dev/null +++ b/examples/edgebadge-neopixel_adc_light.rs @@ -0,0 +1,81 @@ +//! Display light sensor reading on the neopixels. +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::adc::Adc; +use hal::ehal::digital::v1_compat::OldOutputPin; +use hal::prelude::*; +use hal::timer::SpinTimer; +use hal::{clock::GenericClockController, delay::Delay}; +use pac::gclk::pchctrl::GEN_A::GCLK11; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::hsv::SmartLedsWrite; +use smart_leds::hsv::{hsv2rgb, Hsv, RGB8}; +use ws2812_timer_delay as ws2812; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = Pins::new(peripherals.PORT); + + let mut adc1 = Adc::adc1(peripherals.ADC1, &mut peripherals.MCLK, &mut clocks, GCLK11); + let mut light = pins.light.into_function_b(&mut pins.port); + + let timer = SpinTimer::new(4); + let neopixel_pin: OldOutputPin<_> = pins.neopixel.into_push_pull_output(&mut pins.port).into(); + let mut neopixel = ws2812::Ws2812::new(timer, neopixel_pin); + + let mut delay = Delay::new(core.SYST, &mut clocks); + + const NUM_LEDS: usize = 5; + let mut j: u8 = 0; + + loop { + let light_data: u16 = adc1.read(&mut light).unwrap(); + + let pos: usize = if light_data < 100 { + 0 + } else if (light_data >= 147) && (light_data < 1048) { + 1 + } else if (light_data >= 1048) && (light_data < 3048) { + 2 + } else if (light_data >= 3048) && (light_data < 3948) { + 3 + } else { + 4 + }; + + //finally paint the one led wherever the position is + let _ = neopixel.write((0..NUM_LEDS).map(|i| { + if i == pos { + hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 32, + }) + } else { + RGB8::default() + } + })); + + //incremement the hue easing + j = j.wrapping_add(1); + + delay.delay_ms(10u8); + } +} diff --git a/examples/edgebadge-neopixel_button.rs b/examples/edgebadge-neopixel_button.rs new file mode 100644 index 000000000000..4c95338bc0dd --- /dev/null +++ b/examples/edgebadge-neopixel_button.rs @@ -0,0 +1,89 @@ +//! Left and right on 'joystick' controls the first neopixel while it is +//! automatically rotating through the color wheel +//! Select and Start control a second neopixel +//! When they overlap, joystick takes precedence +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +#![no_std] +#![no_main] + +use edgebadge::{self as hal, entry, pac, pins::Keys, Pins}; +use panic_halt as _; + +use hal::prelude::*; +use hal::timer::SpinTimer; +use hal::{clock::GenericClockController, delay::Delay}; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::hsv::SmartLedsWrite; +use smart_leds::hsv::{hsv2rgb, Hsv, RGB8}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core_peripherals = CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut delay = Delay::new(core_peripherals.SYST, &mut clocks); + let mut pins = Pins::new(peripherals.PORT).split(); + + let mut buttons = pins.buttons.init(&mut pins.port); + + // neopixels + let timer = SpinTimer::new(4); + + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + + const NUM_LEDS: usize = 5; + let mut pos_joy: usize = 1; + let mut pos_button: usize = 3; + let mut color_button: u8 = 0; + loop { + for event in buttons.events() { + match event { + Keys::LeftDown => { + pos_joy = pos_joy.saturating_sub(1); + } + Keys::RightDown => { + if pos_joy < 4 { + pos_joy += 1; + } + } + Keys::BDown => { + pos_button = pos_button.saturating_sub(1); + } + Keys::ADown => { + if pos_button < 4 { + pos_button += 1; + } + } + _ => {} + } + } + + //finally paint the two leds at position, accel priority + let _ = neopixel.write((0..NUM_LEDS).map(|i| { + if i == pos_joy || i == pos_button { + hsv2rgb(Hsv { + hue: color_button, + sat: 255, + val: 32, + }) + } else { + RGB8::default() + } + })); + //incremement the wheel easing + color_button = color_button.wrapping_add(1); + + delay.delay_ms(5u8); + } +} diff --git a/examples/edgebadge-neopixel_easing.rs b/examples/edgebadge-neopixel_easing.rs new file mode 100644 index 000000000000..c155c7e28764 --- /dev/null +++ b/examples/edgebadge-neopixel_easing.rs @@ -0,0 +1,87 @@ +//! Blink an led without using the BSP split() method. +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use core::f32::consts::FRAC_PI_2; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::prelude::*; +use hal::timer::SpinTimer; +use hal::trng::Trng; +use micromath::F32Ext; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::hsv::SmartLedsWrite; +use smart_leds::hsv::{hsv2rgb, Hsv, RGB8}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut pins = Pins::new(peripherals.PORT).split(); + let timer = SpinTimer::new(4); + + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + let mut delay = Delay::new(core.SYST, &mut clocks); + + let trng = Trng::new(&mut peripherals.MCLK, peripherals.TRNG); + + const NUM_LEDS: usize = 5; + + loop { + let rand = trng.random_u8(); + let pos: usize = rand.wrapping_rem(5) as usize; //random led + + //slowly enable led + for j in 0..255u8 { + let _ = neopixel.write((0..NUM_LEDS).map(|i| { + if i == pos { + hsv2rgb(Hsv { + hue: rand, + sat: 255, + val: sine_ease_in(j as f32, 0.0, 32.0, 255.0) as u8, + }) + } else { + RGB8::default() + } + })); + delay.delay_ms(5u8); + } + + //slowly disable led - note the reverse .rev() + for j in (0..255u8).rev() { + let _ = neopixel.write((0..NUM_LEDS).map(|i| { + if i == pos { + hsv2rgb(Hsv { + hue: rand, + sat: 255, + val: sine_ease_in(j as f32, 0.0, 32.0, 255.0) as u8, + }) + } else { + RGB8::default() + } + })); + delay.delay_ms(5u8); + } + } +} + +#[inline] +// current step, where oputput starts, where output ends, last step +fn sine_ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 { + -c * (t / d * FRAC_PI_2).cos() + c + b +} diff --git a/examples/edgebadge-neopixel_rainbow_timer.rs b/examples/edgebadge-neopixel_rainbow_timer.rs new file mode 100644 index 000000000000..fffcdc6fa2b4 --- /dev/null +++ b/examples/edgebadge-neopixel_rainbow_timer.rs @@ -0,0 +1,77 @@ +//! Rotate all neopixel leds through a rainbow. Uses a Timer as a timer source. +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml +//! +//! Note: This is jittery these days and probably not a good choice until +//! debugged + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::prelude::*; +use hal::{clock::GenericClockController, delay::Delay, timer::TimerCounter}; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::hsv::SmartLedsWrite; +use smart_leds::hsv::{hsv2rgb, Hsv}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = Pins::new(peripherals.PORT).split(); + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.MCLK); + timer.start(3.mhz()); + + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + let mut delay = Delay::new(core.SYST, &mut clocks); + + loop { + for j in 0..255u8 { + let colors = [ + // stagger the color changes across all 5 leds evenly, 255/5=51 + // and have them safely wrap over when they go above 255 + hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 32, + }), + hsv2rgb(Hsv { + hue: j.wrapping_add(51), + sat: 255, + val: 32, + }), + hsv2rgb(Hsv { + hue: j.wrapping_add(102), + sat: 255, + val: 32, + }), + hsv2rgb(Hsv { + hue: j.wrapping_add(153), + sat: 255, + val: 32, + }), + hsv2rgb(Hsv { + hue: j.wrapping_add(204), + sat: 255, + val: 32, + }), + ]; + neopixel.write(colors.iter().cloned()).unwrap(); + delay.delay_ms(5u8); + } + } +} diff --git a/examples/edgebadge-neopixel_tilt.rs b/examples/edgebadge-neopixel_tilt.rs new file mode 100644 index 000000000000..01a6535efad5 --- /dev/null +++ b/examples/edgebadge-neopixel_tilt.rs @@ -0,0 +1,129 @@ +//! LIS3DH accelerometer example. Move the neopixel led by tilting left and +//! right. +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::prelude::*; +use hal::time::KiloHertz; +use hal::timer::SpinTimer; +use hal::{clock::GenericClockController, delay::Delay}; +use lis3dh::{accelerometer::Accelerometer, Lis3dh}; +use pac::{CorePeripherals, Peripherals}; +use smart_leds::hsv::SmartLedsWrite; +use smart_leds::hsv::{hsv2rgb, Hsv, RGB8}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core_peripherals = CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut delay = Delay::new(core_peripherals.SYST, &mut clocks); + let mut pins = Pins::new(peripherals.PORT).split(); + + // neopixels + let timer = SpinTimer::new(4); + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + + // i2c + let i2c = pins.i2c.init( + &mut clocks, + KiloHertz(400), + peripherals.SERCOM2, + &mut peripherals.MCLK, + &mut pins.port, + ); + + let mut lis3dh = Lis3dh::new(i2c, 0x19).unwrap(); + lis3dh.set_range(lis3dh::Range::G2).unwrap(); + lis3dh.set_datarate(lis3dh::DataRate::Hz_100).unwrap(); + + let mut state = TiltState::new(); + + loop { + let lis = lis3dh.acceleration().unwrap(); + + let (pos, j) = state.update(lis.x); + + // iterate through neopixels and paint the one led + let _ = neopixel.write((0..5).map(|i| { + if i == pos { + hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 32, + }) + } else { + RGB8::default() + } + })); + + //don't update faster than the accell is reading + delay.delay_ms(10u8); + } +} + +pub struct TiltState { + pos: usize, + tilt: i16, + j: u8, +} + +impl TiltState { + // start at the middle pixel + const fn new() -> TiltState { + TiltState { + pos: 2, + tilt: 0, + j: 0, + } + } + + fn update(&mut self, value: i16) -> (usize, u8) { + //what about like.. momentum, more angle or longer its been at angle stops + // slower like.. steps larger so it gets easier. also on a bigger number + // tilt? + + // naive solution.. threshold tilt + // better.. delay filter? + + // actually 2 thresholds, first you have to be tilted enough (gt / lt 1000) to + // be counted + if value > 1000 { + self.tilt += 1; + } else if value < -1000 { + self.tilt -= 1; + } + + // then we need threshold amount of counted tilts to inc/dec position + if self.tilt.abs() > 20 { + //todo clamp is nightly + if self.tilt.is_negative() { + if self.pos > 0 { + self.pos -= 1; + } + } else if self.pos < 4 { + self.pos += 1; + } + self.tilt = 0; + } + + //incremement the hue easing + self.j = self.j.wrapping_add(1); + (self.pos, self.j) + } +} diff --git a/examples/edgebadge-usb_poll.rs b/examples/edgebadge-usb_poll.rs new file mode 100644 index 000000000000..03a5e7a61c6f --- /dev/null +++ b/examples/edgebadge-usb_poll.rs @@ -0,0 +1,77 @@ +//! Makes the pygamer appear as a USB serial port loop back device. +//! Repeats back all characters sent to it, but in upper case. + +#![no_std] +#![no_main] + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use hal::clock::GenericClockController; +use hal::prelude::*; +use pac::{CorePeripherals, Peripherals}; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +#[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.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut pins = Pins::new(peripherals.PORT).split(); + + let usb_bus = pins + .usb + .init(peripherals.USB, &mut clocks, &mut peripherals.MCLK); + + let mut serial = SerialPort::new(&usb_bus); + let mut led = pins.led_pin.into_open_drain_output(&mut pins.port); + + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(); + + loop { + if !usb_dev.poll(&mut [&mut serial]) { + continue; + } + + let mut buf = [0u8; 64]; + + match serial.read(&mut buf) { + Ok(count) if count > 0 => { + let _ = led.set_high(); // Turn on + + // Echo back in upper case + for c in buf[0..count].iter_mut() { + if 0x61 <= *c && *c <= 0x7a { + *c &= !0x20; + } + } + + let mut write_offset = 0; + while write_offset < count { + match serial.write(&buf[write_offset..count]) { + Ok(len) if len > 0 => { + write_offset += len; + } + _ => {} + } + } + } + _ => {} + } + + let _ = led.set_low(); // Turn off + } +} diff --git a/examples/edgebadge-usb_serial.rs b/examples/edgebadge-usb_serial.rs new file mode 100644 index 000000000000..908816aebc72 --- /dev/null +++ b/examples/edgebadge-usb_serial.rs @@ -0,0 +1,132 @@ +#![no_std] +#![no_main] + +//! Makes the pygamer appear as a USB serial port. The color of the +//! neopixel LED can be changed by sending bytes to the serial port. +//! +//! Sending the characters R, G, and O set the LED red, green, and off +//! respectively. For example: +//! $> sudo stty -F /dev/ttyACM0 115200 raw -echo +//! $> sudo bash -c "echo 'R' > /dev/ttyACM0" +//! $> sudo bash -c "echo 'G' > /dev/ttyACM0" +//! $> sudo bash -c "echo 'O' > /dev/ttyACM0" +//! +//! Note leds may appear white during debug. Either build for release or add +//! opt-level = 2 to profile.dev in Cargo.toml + +use edgebadge::{entry, hal, pac, Pins}; +use panic_halt as _; + +use cortex_m::interrupt::free as disable_interrupts; +use cortex_m::peripheral::NVIC; +use hal::clock::GenericClockController; +use hal::timer::SpinTimer; +use hal::usb::UsbBus; +use pac::{interrupt, CorePeripherals, Peripherals}; +use smart_leds::{colors, hsv::RGB8, SmartLedsWrite}; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +#[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.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let mut pins = Pins::new(peripherals.PORT).split(); + + let timer = SpinTimer::new(4); + let mut neopixel = pins.neopixel.init(timer, &mut pins.port); + + let _ = neopixel.write((0..5).map(|_| RGB8::default())); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some( + pins.usb + .init(peripherals.USB, &mut clocks, &mut peripherals.MCLK), + ); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB_OTHER, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT0, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT1, 1); + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + } + + loop { + let pending = disable_interrupts(|_| unsafe { + let pending = PENDING_COLOR; + PENDING_COLOR = None; + pending + }); + if let Some(color) = pending { + let _ = neopixel.write((0..5).map(|_| color)); + } + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; +static mut PENDING_COLOR: 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) { + let last = buf[count - 1] as char; + let color = match last { + 'R' => colors::RED, + 'G' => colors::GREEN, + 'O' => colors::ORANGE, + _ => RGB8::default(), + }; + + PENDING_COLOR = Some(color); + }; + }); + }); + }; +} + +#[interrupt] +fn USB_OTHER() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT0() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT1() { + poll_usb(); +} diff --git a/examples/feather_m0-adalogger.rs b/examples/feather_m0-adalogger.rs new file mode 100644 index 000000000000..13dfecc89df8 --- /dev/null +++ b/examples/feather_m0-adalogger.rs @@ -0,0 +1,209 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use core::fmt::Write; +use core::sync::atomic; + +use cortex_m::interrupt::free as disable_interrupts; +use cortex_m::peripheral::NVIC; +use embedded_sdmmc::{Controller, SdMmcSpi, VolumeIdx}; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use bsp::hal; +use feather_m0 as bsp; + +use bsp::{entry, periph_alias, pin_alias}; +use hal::clock::{ClockGenId, ClockSource, GenericClockController}; +use hal::delay::Delay; +use hal::fugit::RateExtU32; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::rtc; +use hal::usb::UsbBus; + +use heapless::String; + +#[entry] +fn main() -> ! { + // setup basic peripherals + 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 mut delay = Delay::new(core.SYST, &mut clocks); + + // configure the peripherals we'll need + // get the internal 32k running at 1024 Hz for the RTC + let timer_clock = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk3, 32, ClockSource::Osc32k, true) + .unwrap(); + let rtc_clock = clocks.rtc(&timer_clock).unwrap(); + let timer = rtc::Rtc::clock_mode(peripherals.rtc, rtc_clock.freq(), &mut peripherals.pm); + let pins = bsp::Pins::new(peripherals.port); + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + red_led.set_high().unwrap(); + delay.delay_ms(500_u32); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + 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)) + .strings(&[StringDescriptors::new(LangID::EN) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .expect("Failed to set strings") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + red_led.set_low().unwrap(); + delay.delay_ms(500_u32); + + // Now work on the SD peripherals. Slow SPI speed required on init + let spi_sercom = periph_alias!(peripherals.spi_sercom); + let spi = bsp::spi_master( + &mut clocks, + 400_u32.kHz(), + spi_sercom, + &mut peripherals.pm, + pins.sclk, + pins.mosi, + pins.miso, + ); + + red_led.set_high().unwrap(); + delay.delay_ms(500_u32); + + let sd_cd: bsp::SdCd = pins.sd_cd.into(); + let mut sd_cs: bsp::SdCs = pins.sd_cs.into(); + sd_cs.set_high().unwrap(); + + red_led.set_low().unwrap(); + delay.delay_ms(500_u32); + + while !USB_DATA_RECEIVED.load(atomic::Ordering::Relaxed) { + delay.delay_ms(250_u32); + red_led.toggle().unwrap(); + } + + if sd_cd.is_low().unwrap() { + usbserial_write!("No card detected. Waiting...\r\n"); + while sd_cd.is_low().unwrap() { + delay.delay_ms(250_u32); + } + } + usbserial_write!("Card inserted!\r\n"); + delay.delay_ms(250_u32); + + let mut controller = Controller::new(SdMmcSpi::new(spi, sd_cs), timer); + + match controller.device().init() { + Ok(_) => { + // speed up SPI and read out some info + controller + .device() + .spi() + .reconfigure(|c| c.set_baud(4.MHz())); + usbserial_write!("OK!\r\nCard size...\r\n"); + match controller.device().card_size_bytes() { + Ok(size) => usbserial_write!("{} bytes\r\n", size), + Err(e) => usbserial_write!("Err: {:?}\r\n", e), + } + + for i in 0..=3 { + let volume = controller.get_volume(VolumeIdx(i)); + usbserial_write!("volume {:?}\r\n", volume); + if let Ok(volume) = volume { + let root_dir = controller.open_root_dir(&volume).unwrap(); + usbserial_write!("Listing root directory:\r\n"); + controller + .iterate_dir(&volume, &root_dir, |x| { + usbserial_write!("\tFound: {:?}\r\n", x); + }) + .unwrap(); + } + } + } + Err(e) => usbserial_write!("Init err: {:?}!\r\n", e), + } + + usbserial_write!("Done!\r\n"); + loop { + delay.delay_ms(1_000_u32); + red_led.toggle().unwrap(); + } +} + +/// Writes the given message out over USB serial. +#[macro_export] +macro_rules! usbserial_write { + ($($tt:tt)*) => {{ + let mut s: String<1024> = String::new(); + write!(s, $($tt)*).unwrap(); + let message_bytes = s.as_bytes(); + let mut total_written = 0; + while total_written < message_bytes.len() { + let bytes_written = disable_interrupts(|_| unsafe { + match USB_SERIAL.as_mut().unwrap().write( + &message_bytes[total_written..] + ) { + Ok(count) => count, + Err(_) => 0, + } + }); + total_written += bytes_written; + } + }}; +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; +static USB_DATA_RECEIVED: atomic::AtomicBool = atomic::AtomicBool::new(false); + +#[interrupt] +fn USB() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 16]; + if let Ok(count) = serial.read(&mut buf) { + if count > 0 { + USB_DATA_RECEIVED.store(true, atomic::Ordering::Relaxed); + } + } + }; + }; + }; +} diff --git a/examples/feather_m0-adc.rs b/examples/feather_m0-adc.rs new file mode 100644 index 000000000000..6194e9d26b28 --- /dev/null +++ b/examples/feather_m0-adc.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] + +use atsamd_hal::adc::AdcBuilder; +use feather_m0 as bsp; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Adc, Prescaler, Resolution}, + clock::GenericClockController, +}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + let gclk0 = clocks.gclk0(); + let adc_clock = clocks.adc(&gclk0).unwrap(); + + let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) + .with_clock_cycles_per_sample(5) + .with_clock_divider(Prescaler::Div128) + .with_vref(atsamd_hal::adc::Reference::Intvcc0) + .enable(peripherals.adc, &mut peripherals.pm, &adc_clock) + .unwrap(); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let res = adc.read(&mut adc_pin); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("ADC value: {}", read).unwrap(); + } +} diff --git a/examples/feather_m0-async_dmac.rs b/examples/feather_m0-async_dmac.rs new file mode 100644 index 000000000000..9cb8efa8fb95 --- /dev/null +++ b/examples/feather_m0-async_dmac.rs @@ -0,0 +1,73 @@ +//! This example shows a safe API to +//! execute a memory-to-memory DMA transfer + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +atsamd_hal::bind_interrupts!(struct Irqs { + DMAC => atsamd_hal::dmac::InterruptHandler; +}); + +use bsp::hal; +use bsp::pac; +use feather_m0 as bsp; +use hal::{ + clock::GenericClockController, + dmac::{DmaController, PriorityLevel, TriggerAction, TriggerSource}, +}; + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::CorePeripherals::take().unwrap(); + + let _clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + + // Initialize DMA Controller + let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); + + // Turn dmac into an async controller + let mut dmac = dmac.into_future(crate::Irqs); + // Get individual handles to DMA channels + let channels = dmac.split(); + + // Initialize DMA Channel 0 + let mut channel = channels.0.init(PriorityLevel::Lvl0); + + let mut source = [0xff; 100]; + let mut dest = [0x0; 100]; + + defmt::info!( + "Launching a DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}", + &source, + &dest + ); + + channel + .transfer_future( + &mut source, + &mut dest, + TriggerSource::Disable, + TriggerAction::Block, + ) + .await + .unwrap(); + + defmt::info!( + "Finished DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}", + &source, + &dest + ); + + loop { + cortex_m::asm::wfi(); + } +} diff --git a/examples/feather_m0-async_eic.rs b/examples/feather_m0-async_eic.rs new file mode 100644 index 000000000000..50f9a1725992 --- /dev/null +++ b/examples/feather_m0-async_eic.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +use bsp::pac; +use bsp::{hal, pin_alias}; +use feather_m0 as bsp; +use hal::{ + clock::{enable_internal_32kosc, ClockGenId, ClockSource, GenericClockController}, + ehal::digital::StatefulOutputPin, + eic::{Eic, Sense}, + gpio::{Pin, PullUpInterrupt}, +}; + +atsamd_hal::bind_interrupts!(struct Irqs { + EIC => atsamd_hal::eic::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::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 red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + let _internal_clock = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk2, 1, ClockSource::Osc32k, false) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk2, true); + + enable_internal_32kosc(&mut peripherals.sysctrl); + + // Configure a clock for the EIC peripheral + let gclk2 = clocks.get_gclk(ClockGenId::Gclk2).unwrap(); + let eic_clock = clocks.eic(&gclk2).unwrap(); + + let eic_channels = Eic::new(&mut peripherals.pm, eic_clock, peripherals.eic) + .into_future(Irqs) + .split(); + + let button: Pin<_, PullUpInterrupt> = pins.d10.into(); + let mut extint = eic_channels.2.with_pin(button); + + loop { + // Here we show straight falling edge detection without + extint.wait(Sense::Fall).await; + defmt::info!("Falling edge detected"); + red_led.toggle().unwrap(); + } +} diff --git a/examples/feather_m0-async_i2c.rs b/examples/feather_m0-async_i2c.rs new file mode 100644 index 000000000000..7c986c6b77f2 --- /dev/null +++ b/examples/feather_m0-async_i2c.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +use bsp::hal; +use bsp::pac; +use feather_m0 as bsp; +use hal::ehal_async::i2c::I2c; +use hal::{ + clock::GenericClockController, + dmac::{DmaController, PriorityLevel}, + fugit::MillisDuration, + prelude::*, + sercom::{i2c, Sercom3}, +}; +use rtic_monotonics::systick::Systick; + +atsamd_hal::bind_interrupts!(struct Irqs { + SERCOM3 => atsamd_hal::sercom::i2c::InterruptHandler; + DMAC => atsamd_hal::dmac::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::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); + + // Take SDA and SCL + let (sda, scl) = (pins.sda, pins.scl); + let i2c_sercom = bsp::periph_alias!(peripherals.i2c_sercom); + + // Initialize DMA Controller + let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); + + // Turn dmac into an async controller + let mut dmac = dmac.into_future(Irqs); + // Get individual handles to DMA channels + let channels = dmac.split(); + + // Initialize DMA Channel 0 + let channel0 = channels.0.init(PriorityLevel::Lvl0); + + let gclk0 = clocks.gclk0(); + let sercom3_clock = &clocks.sercom3_core(&gclk0).unwrap(); + let pads = i2c::Pads::new(sda, scl); + let mut i2c = i2c::Config::new(&peripherals.pm, i2c_sercom, pads, sercom3_clock.freq()) + .baud(100.kHz()) + .enable() + .into_future(Irqs) + .with_dma_channel(channel0); + + loop { + defmt::info!("Sending 0x00 to I2C device..."); + // This test is based on the BMP388 barometer. Feel free to use any I2C + // peripheral you have on hand. + i2c.write(0x76, &[0x00]).await.unwrap(); + + let mut buffer = [0xff; 4]; + i2c.read(0x76, &mut buffer).await.unwrap(); + defmt::info!("Read buffer: {:#x}", buffer); + Systick::delay(MillisDuration::::from_ticks(500).convert()).await; + } +} diff --git a/examples/feather_m0-async_spi.rs b/examples/feather_m0-async_spi.rs new file mode 100644 index 000000000000..ddcae321cf09 --- /dev/null +++ b/examples/feather_m0-async_spi.rs @@ -0,0 +1,80 @@ +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +use bsp::hal; +use bsp::pac; +use feather_m0 as bsp; +use hal::ehal_async::spi::SpiBus; +use hal::{ + clock::GenericClockController, + dmac::{DmaController, PriorityLevel}, + fugit::MillisDuration, + prelude::*, + sercom::Sercom4, +}; +use rtic_monotonics::systick::Systick; + +atsamd_hal::bind_interrupts!(struct Irqs { + SERCOM4 => atsamd_hal::sercom::spi::InterruptHandler; + DMAC => atsamd_hal::dmac::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::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); + + // Take SPI pins + let (miso, mosi, sclk) = (pins.miso, pins.mosi, pins.sclk); + let spi_sercom = bsp::periph_alias!(peripherals.spi_sercom); + + // Initialize DMA Controller + let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); + + // Turn dmac into an async controller + let mut dmac = dmac.into_future(Irqs); + // Get individual handles to DMA channels + let channels = dmac.split(); + + // Initialize DMA Channels 0 and 1 + let channel0 = channels.0.init(PriorityLevel::Lvl0); + let channel1 = channels.1.init(PriorityLevel::Lvl0); + + let mut spi = bsp::spi_master( + &mut clocks, + 100.kHz(), + spi_sercom, + &mut peripherals.pm, + sclk, + mosi, + miso, + ) + .into_future(Irqs) + .with_dma_channels(channel0, channel1); + + loop { + defmt::info!("Sending 0x00 to SPI device..."); + // This test is based on the BMP388 barometer. Feel free to use any I2C + // peripheral you have on hand. + spi.write(&[0x00]).await.unwrap(); + + defmt::info!("Sent 0x00."); + + let mut buffer = [0xff; 4]; + spi.read(&mut buffer).await.unwrap(); + defmt::info!("Read buffer: {:#x}", buffer); + Systick::delay(MillisDuration::::from_ticks(500).convert()).await; + } +} diff --git a/examples/feather_m0-async_timer.rs b/examples/feather_m0-async_timer.rs new file mode 100644 index 000000000000..589abd31f581 --- /dev/null +++ b/examples/feather_m0-async_timer.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{hal, pac, pin_alias}; +use feather_m0 as bsp; +use hal::{ + clock::{enable_internal_32kosc, ClockGenId, ClockSource, GenericClockController}, + ehal::digital::StatefulOutputPin, + fugit::MillisDurationU32, + pac::Tc4, + timer::TimerCounter, +}; + +atsamd_hal::bind_interrupts!(struct Irqs { + TC4 => atsamd_hal::timer::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::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 red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + enable_internal_32kosc(&mut peripherals.sysctrl); + let timer_clock = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk2, 1, ClockSource::Osc32k, false) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk2, true); + + // configure a clock for the TC4 and TC5 peripherals + let tc45 = &clocks.tc4_tc5(&timer_clock).unwrap(); + + // instantiate a timer object for the TC4 peripheral + let timer = TimerCounter::tc4_(tc45, peripherals.tc4, &mut peripherals.pm); + let mut timer = timer.into_future(Irqs); + + loop { + timer + .delay(MillisDurationU32::from_ticks(500).convert()) + .await; + red_led.toggle().unwrap(); + } +} diff --git a/examples/feather_m0-async_uart.rs b/examples/feather_m0-async_uart.rs new file mode 100644 index 000000000000..fb555ac6aaa0 --- /dev/null +++ b/examples/feather_m0-async_uart.rs @@ -0,0 +1,101 @@ +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +use bsp::{hal, pac, periph_alias, pin_alias}; +use feather_m0 as bsp; +use hal::{ + clock::GenericClockController, + dmac::{Ch0, Ch1, DmaController, PriorityLevel}, + fugit::MillisDuration, + prelude::*, + sercom::{ + uart::{Config, UartFutureRxDuplexDma, UartFutureTxDuplexDma}, + Sercom0, + }, +}; +use rtic_monotonics::systick::Systick; + +atsamd_hal::bind_interrupts!(struct Irqs { + SERCOM0 => atsamd_hal::sercom::uart::InterruptHandler; + DMAC => atsamd_hal::dmac::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::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); + + // Take rx and tx pins + let (uart_rx, uart_tx) = (pin_alias!(pins.uart_rx), pin_alias!(pins.uart_tx)); + let uart_sercom = periph_alias!(peripherals.uart_sercom); + + // Initialize DMA Controller + let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); + // Turn dmac into an async controller + let mut dmac = dmac.into_future(Irqs); + // Get individual handles to DMA channels + let channels = dmac.split(); + + // Initialize DMA Channels 0 and 1 + let channel0 = channels.0.init(PriorityLevel::Lvl0); + let channel1 = channels.1.init(PriorityLevel::Lvl0); + + let (uart_rx, uart_tx) = bsp::uart( + &mut clocks, + 9600.Hz(), + uart_sercom, + &mut peripherals.pm, + uart_rx, + uart_tx, + ) + .into_future(Irqs) + .with_rx_dma_channel(channel0) + .with_tx_dma_channel(channel1) + .split(); + + // For embassy-executor, spawning multiple tasks on the same executor requires + // either: + // * Tuning the task arena size either via a Cargo feature or the + // `EMBASSY_EXECUTOR_TASK_ARENA_SIZE` environment variable + // * Using the `nightly` Cargo feature along with + // #![feature(type_alias_impl_trait)] + spawner.spawn(send_bytes(uart_tx)).unwrap(); + spawner.spawn(receive_bytes(uart_rx)).unwrap(); +} + +#[embassy_executor::task] +async fn send_bytes(mut uart_tx: UartFutureTxDuplexDma, Ch1>) { + loop { + uart_tx.write(b"Hello, world!").await.unwrap(); + defmt::info!("Sent 10 bytes"); + Systick::delay(MillisDuration::::from_ticks(500).convert()).await; + } +} + +#[embassy_executor::task] +async fn receive_bytes(mut uart_rx: UartFutureRxDuplexDma, Ch0>) { + uart_rx.as_mut().flush_rx_buffer(); + + loop { + let mut buf = [0x00; 10]; + match uart_rx.read(&mut buf).await { + Ok(()) => defmt::info!("read {:#x}", &buf), + Err(_) => { + defmt::error!("UART Error."); + // Flusing the RX buffer may drop a few bytes. + uart_rx.as_mut().flush_rx_buffer(); + } + } + } +} diff --git a/examples/feather_m0-blinky_basic.rs b/examples/feather_m0-blinky_basic.rs new file mode 100644 index 000000000000..a75962c5c499 --- /dev/null +++ b/examples/feather_m0-blinky_basic.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::hal; +use bsp::pac; +use feather_m0 as bsp; + +use bsp::{entry, pin_alias}; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::prelude::*; +use pac::{CorePeripherals, Peripherals}; + +#[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 red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + let mut delay = Delay::new(core.SYST, &mut clocks); + loop { + delay.delay_ms(200u8); + red_led.set_high().unwrap(); + delay.delay_ms(200u8); + red_led.set_low().unwrap(); + } +} diff --git a/examples/feather_m0-blinky_embassy.rs b/examples/feather_m0-blinky_embassy.rs new file mode 100644 index 000000000000..b0af00c0380c --- /dev/null +++ b/examples/feather_m0-blinky_embassy.rs @@ -0,0 +1,55 @@ +//! Uses RTIC with systick to blink a led in an asynchronous fashion. +#![no_std] +#![no_main] + +use feather_m0 as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{hal, pac, pin_alias}; +use hal::clock::{ClockGenId, ClockSource, GenericClockController}; +use hal::prelude::*; +use hal::rtc::rtic::rtc_clock; + +// We can use the RTIC monotonic with embassy +hal::rtc_monotonic!(Mono, rtc_clock::ClockCustom<8_192>); + +// However, to do so, we need to define this, which is normally defined within +// RTIC. This sets the RTC monotonic interrupt priority to be the most +// important. +#[no_mangle] +static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = 1; + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::CorePeripherals::take().unwrap(); + let pins = bsp::Pins::new(peripherals.port); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + + // Set the RTC clock to use a 8.192 kHz clock derived from the external 32 kHz + // oscillator. + let rtc_clock_src = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk2, 4, ClockSource::Xosc32k, false) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk2, true); + let _ = clocks.rtc(&rtc_clock_src).unwrap(); + + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // Start the monotonic + Mono::start(peripherals.rtc); + + loop { + let _ = red_led.toggle(); + Mono::delay(1u64.secs()).await; + } +} diff --git a/examples/feather_m0-blinky_rtic.rs b/examples/feather_m0-blinky_rtic.rs new file mode 100644 index 000000000000..823e3d065522 --- /dev/null +++ b/examples/feather_m0-blinky_rtic.rs @@ -0,0 +1,77 @@ +//! Uses RTIC with a software task to blink an LED. +#![no_std] +#![no_main] + +use bsp::hal; +use feather_m0 as bsp; +use hal::rtc::rtic::rtc_clock; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +hal::rtc_monotonic!(Mono, rtc_clock::ClockCustom<8_192>); + +#[rtic::app(device = bsp::pac, dispatchers = [EVSYS])] +mod app { + use super::*; + use bsp::{hal, pin_alias}; + use hal::clock::{ClockGenId, ClockSource, GenericClockController}; + use hal::pac::Peripherals; + use hal::prelude::*; + + #[local] + struct Local {} + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + red_led: bsp::RedLed, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + let mut peripherals: Peripherals = cx.device; + let pins = bsp::Pins::new(peripherals.port); + let mut core: rtic::export::Peripherals = cx.core; + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + + // Set the RTC clock to use a 8.192 kHz clock derived from the external 32 kHz + // oscillator. + let rtc_clock_src = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk2, 4, ClockSource::Xosc32k, false) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk2, true); + let _ = clocks.rtc(&rtc_clock_src).unwrap(); + + let red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // Start the monotonic + Mono::start(peripherals.rtc); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + // Start the blink task + blink::spawn().unwrap(); + + (Shared { red_led }, Local {}) + } + + #[task(shared = [red_led])] + async fn blink(mut cx: blink::Context) { + loop { + // If the LED were a local resource, the lock would not be necessary + cx.shared.red_led.lock(|led| led.toggle().unwrap()); + + Mono::delay(1u64.secs()).await; + } + } +} diff --git a/examples/feather_m0-clock.rs b/examples/feather_m0-clock.rs new file mode 100644 index 000000000000..f32db4f23b5c --- /dev/null +++ b/examples/feather_m0-clock.rs @@ -0,0 +1,189 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use cortex_m::interrupt::free as disable_interrupts; +use cortex_m::peripheral::NVIC; +use heapless::String; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use bsp::hal; +use bsp::pac; +use feather_m0 as bsp; + +use bsp::{entry, pin_alias}; +use hal::clock::{ClockGenId, ClockSource, GenericClockController}; +use hal::delay::Delay; +use hal::prelude::*; +use hal::rtc; +use hal::usb::UsbBus; +use pac::{interrupt, CorePeripherals, Peripherals}; + +#[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 mut delay = Delay::new(core.SYST, &mut clocks); + let pins = bsp::Pins::new(peripherals.port); + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // get the internal 32k running at 1024 Hz for the RTC + let timer_clock = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk3, 32, ClockSource::Osc32k, true) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk3, true); + let rtc_clock = clocks.rtc(&timer_clock).unwrap(); + let rtc = rtc::Rtc::clock_mode(peripherals.rtc, rtc_clock.freq(), &mut peripherals.pm); + + unsafe { + RTC = Some(rtc); + } + + // initialize USB + 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)) + .strings(&[StringDescriptors::new(LangID::EN) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .expect("Failed to set strings") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + unsafe { + core.NVIC.set_priority(interrupt::USB, 1); + NVIC::unmask(interrupt::USB); + } + + // Print the time forever! + loop { + red_led.toggle().ok(); + let time = + disable_interrupts(|_| unsafe { RTC.as_mut().map(|rtc| rtc.current_time()) }).unwrap(); + + let mut data = String::<16>::new(); + write!( + data, + "{:02}:{:02}:{:02}\r\n", + time.hours, time.minutes, time.seconds + ) + .ok() + .unwrap(); + write_serial(data.as_bytes()); + + delay.delay_ms(1_000_u32); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; +static mut RTC: Option> = None; + +fn write_serial(bytes: &[u8]) { + unsafe { + if let Some(serial) = USB_SERIAL.as_mut() { + serial.write(bytes).unwrap(); + }; + } +} + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 32]; + + if let Ok(count) = serial.read(&mut buf) { + let mut buffer: &[u8] = &buf[..count]; + // echo to terminal + serial.write(buffer).unwrap(); + + // Look for setting of time + while buffer.len() > 5 { + match timespec(buffer) { + Ok((remaining, time)) => { + buffer = remaining; + disable_interrupts(|_| { + if let Some(rtc) = RTC.as_mut() { + rtc.set_time(rtc::Datetime { + seconds: time.second as u8, + minutes: time.minute as u8, + hours: time.hour as u8, + day: 0, + month: 0, + year: 0, + }); + }; + }); + } + _ => break, + }; + } + }; + }; + }; + }; +} + +#[interrupt] +fn USB() { + poll_usb(); +} + +#[derive(Clone)] +pub struct Time { + hour: usize, + minute: usize, + second: usize, +} + +use drogue_nom_utils::parse_usize; +use nom::{char, do_parse, named, opt, tag}; + +named!( + pub timespec