diff --git a/boards/feather_m4_can/.cargo/config.toml b/boards/feather_m4_can/.cargo/config.toml new file mode 100644 index 00000000000..eb96b4c4110 --- /dev/null +++ b/boards/feather_m4_can/.cargo/config.toml @@ -0,0 +1,15 @@ +# vim:ft=toml: +[target.thumbv7em-none-eabihf] +runner = 'arm-none-eabi-gdb' +# runner = 'probe-run --chip ATSAMD51J19A' + +[build] +target = "thumbv7em-none-eabihf" +rustflags = [ + + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", + + "-C", "link-arg=-Tlink.x", +] diff --git a/boards/feather_m4_can/CHANGELOG.md b/boards/feather_m4_can/CHANGELOG.md new file mode 100644 index 00000000000..24393864da8 --- /dev/null +++ b/boards/feather_m4_can/CHANGELOG.md @@ -0,0 +1,27 @@ +# Unreleased + +# v0.10.1 + +- Update to `atsamd-hal` version `0.15.1` +- Make use of `bsp_peripherals` macro + +# v0.10.0 + +- Update `lib.rs` and examples to reflect removal of `v1` APIs and promotion of `v2` APIs +- Add an `i2c` example +- Update `i2c_master` convenience function to use the new `sercom::v2::i2c` API +- Updated to 2021 edition, updated dependencies, removed unused dependencies (#562) + +# v0.9.0 + +- replace deprecated `SpinTimer` with `TimerCounter` in the `neopixel_rainbow` example +- remove extraneous `embedded-hal` dependencies from BSPs +- cleanup `cortex_m` dependency +- move `usbd-x` crates used only in examples to `[dev-dependencies]` +- removed unnecessary dependency on `nb` (#510) +- add Public Key Cryptography Controller (PUKCC) example (#486) +- Update to use refactored SPI module (#467) + +--- + +Changelog tracking started at v0.8.0 diff --git a/boards/feather_m4_can/Cargo.toml b/boards/feather_m4_can/Cargo.toml new file mode 100644 index 00000000000..e3c70be87ed --- /dev/null +++ b/boards/feather_m4_can/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "feather_m4_can" +version = "0.10.1" +edition = "2021" +authors = ["Theodore DeRego "] +description = "Board Support crate for the Adafruit Feather M4 CAN" +keywords = ["no-std", "arm", "cortex-m", "embedded-hal"] +categories = ["embedded", "hardware-support", "no-std"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/atsamd-rs/atsamd" +readme = "README.md" +documentation = "https://atsamd-rs.github.io/atsamd/atsamd51j/feather_m4/" + +# for cargo flash +[package.metadata] +chip = "ATSAMD51J19A" + +[dependencies.cortex-m-rt] +version = "0.7" +optional = true + +[dependencies.atsamd-hal] +path = "../../hal" +version = "0.15.1" +default-features = false + +[dependencies.usb-device] +version = "0.2" +optional = true + +[dev-dependencies] +cortex-m = "0.7" +usbd-serial = "0.1" +panic-halt = "0.2" +panic-semihosting = "0.5" +smart-leds = "0.3" +ws2812-timer-delay = "0.3" +heapless = "0.7" + +[features] +# ask the HAL to enable atsamd51j support +default = ["rt", "atsamd-hal/samd51j", "atsamd-hal/samd51"] +rt = ["cortex-m-rt", "atsamd-hal/samd51j-rt"] +unproven = ["atsamd-hal/unproven"] +usb = ["atsamd-hal/usb", "usb-device"] +dma = ["atsamd-hal/dma", "unproven"] +max-channels = ["dma", "atsamd-hal/dma"] + + +[profile.dev] +incremental = false +codegen-units = 1 +debug = true +lto = true + +[profile.release] +debug = true +lto = true +opt-level = "s" diff --git a/boards/feather_m4_can/README.md b/boards/feather_m4_can/README.md new file mode 100644 index 00000000000..87548bba6eb --- /dev/null +++ b/boards/feather_m4_can/README.md @@ -0,0 +1,26 @@ +# Adafruit Feather M4 CAN Board Support Crate + +This crate provides a type-safe API for working with the [Adafruit Feather M4 CAN +board](https://www.adafruit.com/product/4759). + +## Prerequisites +* Install the cross compile toolchain `rustup target add thumbv7em-none-eabihf` +* Install [cargo-hf2 the hf2 bootloader flasher tool](https://crates.io/crates/cargo-hf2) however your platform requires + +## Uploading an example +Check out the repository for examples: + +https://github.com/atsamd-rs/atsamd/tree/master/boards/feather_m4_can/examples + +* Be in this directory `cd boards/feather_m4` +* Put your device in bootloader mode usually by hitting the reset button twice. +* Build and upload in one step +``` +$ cargo hf2 --release --example blinky_basic + Finished release [optimized + debuginfo] target(s) in 0.19s + Searching for a connected device with known vid/pid pair. + Trying Ok(Some("Adafruit Industries")) Ok(Some("PyBadge")) + Flashing "/Users/User/atsamd/boards/feather_m4/target/thumbv7em-none-eabihf/release/examples/blinky_basic" + Finished in 0.079s +$ +``` diff --git a/boards/feather_m4_can/build.rs b/boards/feather_m4_can/build.rs new file mode 100644 index 00000000000..4bed4688f2c --- /dev/null +++ b/boards/feather_m4_can/build.rs @@ -0,0 +1,16 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +fn main() { + if env::var_os("CARGO_FEATURE_RT").is_some() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=memory.x"); + } + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/boards/feather_m4_can/examples/blinky_basic.rs b/boards/feather_m4_can/examples/blinky_basic.rs new file mode 100644 index 00000000000..26d182ed130 --- /dev/null +++ b/boards/feather_m4_can/examples/blinky_basic.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] + +use feather_m4_can as bsp; +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::hal; +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.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + let mut delay = Delay::new(core.SYST, &mut clocks); + loop { + delay.delay_ms(2000u16); + red_led.set_high().unwrap(); + delay.delay_ms(2000u16); + red_led.set_low().unwrap(); + } +} diff --git a/boards/feather_m4_can/examples/neopixel_rainbow.rs b/boards/feather_m4_can/examples/neopixel_rainbow.rs new file mode 100644 index 00000000000..656afbc7b05 --- /dev/null +++ b/boards/feather_m4_can/examples/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 + +use bsp::hal; +use feather_m4_can as bsp; + +#[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::timer::*; + +use smart_leds::{ + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; +use ws2812_timer_delay::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 pins = bsp::Pins::new(peripherals.PORT); + let mut delay = Delay::new(core.SYST, &mut clocks); + + 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 neopixel_pin = pins.neopixel.into_push_pull_output(); + let mut neopixel_power_pin = pins.neopixel_pwr.into_push_pull_output(); + neopixel_power_pin.set_high().unwrap(); + let mut neopixel = Ws2812::new(timer, neopixel_pin); + + loop { + for j in 0..255u8 { + let colors = [hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 2, + })]; + neopixel.write(colors.iter().cloned()).unwrap(); + delay.delay_ms(5u8); + } + } +} diff --git a/boards/feather_m4_can/memory.x b/boards/feather_m4_can/memory.x new file mode 100644 index 00000000000..073403cecc8 --- /dev/null +++ b/boards/feather_m4_can/memory.x @@ -0,0 +1,8 @@ +MEMORY +{ + /* Leave 16k for the default bootloader on the Feather M4 */ + FLASH (rx) : ORIGIN = 0x00000000 + 16K, LENGTH = 512K - 16K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K +} +_stack_start = ORIGIN(RAM) + LENGTH(RAM); + diff --git a/boards/feather_m4_can/src/lib.rs b/boards/feather_m4_can/src/lib.rs new file mode 100644 index 00000000000..3cfa9d3662c --- /dev/null +++ b/boards/feather_m4_can/src/lib.rs @@ -0,0 +1,297 @@ +#![no_std] +#![deny(missing_docs)] + +//! Board support crate for Adafruit's Feather M4 Express, +//! an ATSAMD51-based board in Feather form factor. + +#[cfg(feature = "rt")] +pub use cortex_m_rt::entry; + +pub use atsamd_hal as hal; +pub use hal::ehal; +pub use hal::pac; + +use hal::clock::GenericClockController; +use hal::sercom::{ + i2c, spi, + uart::{self, BaudMode, Oversampling}, + IoSet1, UndocIoSet1, +}; +use hal::time::Hertz; + +#[cfg(feature = "usb")] +use hal::usb::usb_device::bus::UsbBusAllocator; +#[cfg(feature = "usb")] +pub use hal::usb::UsbBus; + +hal::bsp_peripherals!( + SERCOM1 { SpiSercom } + SERCOM2 { I2cSercom } + SERCOM5 { UartSercom } +); + +hal::bsp_pins!( + PA02 { + /// Analog pin 0. Can act as a true analog output + /// as it has a DAC (which is not currently supported + /// by this hal) as well as input. + name: a0, + } + PA05 { + /// Analog Pin 1 + name: a1, + } + PB08 { + /// Analog Pin 2 + name: a2, + } + PB09 { + /// Analog Pin 3 + name: a3, + } + PA04 { + /// Analog Pin 4 + name: a4, + } + PA06 { + /// Analog Pin 5 + name: a5, + } + PB01 { + /// Analog Vdiv (1/2 resistor divider for monitoring the battery) + name: battery, + } + + PB17 { + /// Pin 0, UART rx + name: d0, + aliases: { + AlternateC: UartRx + } + } + PB16 { + /// Pin 1, UART tx + name: d1, + aliases: { + AlternateC: UartTx + } + } + PA14 { + /// Pin 4, PWM capable + name: d4, + } + PA16 { + /// Pin 5, PWM capable + name: d5, + } + PA18 { + /// Pin 6, PWM capable + name: d6, + } + PB02 { + /// Neopixel Pin + name: neopixel, + } + PB03 { + /// Neopixel Pin + name: neopixel_pwr, + } + PA19 { + /// Pin 9, PWM capable. Also analog input (A7) + name: d9, + } + PA20 { + /// Pin 10, PWM capable + name: d10, + } + PA21 { + /// Pin 11, PWM capable + name: d11, + } + PA22 { + /// Pin 12, PWM capable + name: d12, + } + PA23 { + /// Pin 13, which is also attached to the red LED. PWM capable. + name: d13, + aliases: { + PushPullOutput: RedLed, + AlternateE: RedLedPwm + } + } + PA12 { + /// The I2C data line + name: sda, + aliases: { + AlternateC: Sda + } + } + PA13 { + /// The I2C clock line + name: scl, + aliases: { + AlternateC: Scl + } + } + PA17 { + /// The SPI SCK + name: sck, + aliases: { + AlternateC: Sclk + } + } + PB23 { + /// The SPI MOSI + name: mosi, + aliases: { + AlternateC: Mosi + } + } + PB22 { + /// The SPI MISO + name: miso, + aliases: { + AlternateC: Miso + } + } + PA24 { + /// The USB D- pad + name: usb_dm, + aliases: { + AlternateG: UsbDm + } + } + PA25 { + /// The USB D+ pad + name: usb_dp, + aliases: { + AlternateG: UsbDp + } + } + PB12 { + /// CAN1_S + name: can1_s, + } + PB13 { + /// BOOST Enable + name: boost_en, + } + PB14 { + /// CAN1_TX + name: can1_tx, + } + PB15 { + /// CAN1_RX + name: can1_rx, + } +); + +/// SPI pads for the labelled SPI peripheral +/// +/// You can use these pads with other, user-defined [`spi::Config`]urations. +pub type SpiPads = spi::Pads; + +/// SPI master for the labelled SPI peripheral +/// +/// This type implements [`FullDuplex`](ehal::spi::FullDuplex). +pub type Spi = spi::Spi, spi::Duplex>; + +/// Convenience for setting up the labelled SPI peripheral. +/// This powers up SERCOM1 and configures it for use as an +/// SPI Master in SPI Mode 0. +pub fn spi_master( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: SpiSercom, + mclk: &mut pac::MCLK, + sclk: impl Into, + mosi: impl Into, + miso: impl Into, +) -> Spi { + let gclk0 = clocks.gclk0(); + let clock = clocks.sercom1_core(&gclk0).unwrap(); + let freq = clock.freq(); + let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); + let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); + spi::Config::new(mclk, sercom, pads, freq) + .baud(baud) + .spi_mode(spi::MODE_0) + .enable() +} + +/// I2C pads for the labelled I2C peripheral +/// +/// You can use these pads with other, user-defined [`i2c::Config`]urations. +pub type I2cPads = i2c::Pads; + +/// I2C master for the labelled I2C peripheral +/// +/// This type implements [`Read`](ehal::blocking::i2c::Read), +/// [`Write`](ehal::blocking::i2c::Write) and +/// [`WriteRead`](ehal::blocking::i2c::WriteRead). +pub type I2c = i2c::I2c>; + +/// Convenience for setting up the labelled SDA, SCL pins to +/// operate as an I2C master running at the specified frequency. +pub fn i2c_master( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: I2cSercom, + mclk: &mut pac::MCLK, + sda: impl Into, + scl: impl Into, +) -> I2c { + let gclk0 = clocks.gclk0(); + let clock = &clocks.sercom2_core(&gclk0).unwrap(); + let freq = clock.freq(); + let baud = baud.into(); + let pads = i2c::Pads::new(sda.into(), scl.into()); + i2c::Config::new(mclk, sercom, pads, freq) + .baud(baud) + .enable() +} + +/// UART pads for the labelled RX & TX pins +pub type UartPads = uart::Pads; + +/// UART device for the labelled RX & TX pins +pub type Uart = uart::Uart, uart::Duplex>; + +/// Convenience for setting up the labelled RX, TX pins to +/// operate as a UART device running at the specified baud. +pub fn uart( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: UartSercom, + mclk: &mut pac::MCLK, + rx: impl Into, + tx: impl Into, +) -> Uart { + let gclk0 = clocks.gclk0(); + + let clock = &clocks.sercom5_core(&gclk0).unwrap(); + let baud = baud.into(); + let pads = uart::Pads::default().rx(rx.into()).tx(tx.into()); + uart::Config::new(mclk, sercom, pads, clock.freq()) + .baud(baud, BaudMode::Fractional(Oversampling::Bits16)) + .enable() +} + +#[cfg(feature = "usb")] +/// Convenience function for setting up USB +pub fn usb_allocator( + dm: impl Into, + dp: impl Into, + usb: pac::USB, + clocks: &mut GenericClockController, + mclk: &mut pac::MCLK, +) -> UsbBusAllocator { + use pac::gclk::{genctrl::SRC_A, pchctrl::GEN_A}; + + clocks.configure_gclk_divider_and_source(GEN_A::GCLK2, 1, SRC_A::DFLL, false); + let usb_gclk = clocks.get_gclk(GEN_A::GCLK2).unwrap(); + let usb_clock = &clocks.usb(&usb_gclk).unwrap(); + let (dm, dp) = (dm.into(), dp.into()); + UsbBusAllocator::new(UsbBus::new(usb_clock, mclk, dm, dp, usb)) +} diff --git a/crates.json b/crates.json index 609d87809bf..c0c23249404 100644 --- a/crates.json +++ b/crates.json @@ -36,6 +36,10 @@ "tier": 1, "build": "cargo build --examples --all-features" }, + "feather_m4_can": { + "tier": 1, + "build": "cargo build --examples --all-features" + }, "gemma_m0": { "tier": 2, "build": "cargo build --examples --features=unproven"