Skip to content

Commit

Permalink
Add feather_m4_can board, add mcan example
Browse files Browse the repository at this point in the history
- the new `feather_m4_can` board is added as a clone
  of `feather_m4`  with just the chip changed
  ATSAM(D->E)51J

- adapt pinmux: alternate function pins for CAN1
  connecting to TCAN1051 transceiver

- adapt `mcan` example from `xplain` board replacing
  button triggering by a periodic task
  • Loading branch information
Felix-El committed Apr 27, 2023
1 parent e8fbf5d commit 5cb07f8
Show file tree
Hide file tree
Showing 25 changed files with 3,313 additions and 0 deletions.
15 changes: 15 additions & 0 deletions boards/feather_m4_can/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# vim:ft=toml:
[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
#runner = 'probe-run --chip ATSAME51J19A'

[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",
]
9 changes: 9 additions & 0 deletions boards/feather_m4_can/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# v0.1.0

- The board is added as a clone of `feather_m4` with chip changed ATSAM(D->E)51J
- adapt pinmux: alternate function pins for CAN1 connecting to TCAN1051 transceiver
- clone `mcan` example from `atsame54_xpro` board (using periodic task instead of button)

---

Changelog tracking started at v0.1.0
101 changes: 101 additions & 0 deletions boards/feather_m4_can/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
[package]
name = "feather_m4_can"
version = "0.1.0"
edition = "2021"
authors = ["Theodore DeRego <[email protected]>"]
description = "Board Support crate for the Adafruit Feather M4 CAN"
keywords = ["no-std", "arm", "cortex-m", "embedded-hal", "can"]
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_can/"

# for cargo flash
[package.metadata]
chip = "ATSAME51J19A"

[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]
mcan = "0.2"
dwt-systick-monotonic = "1.1"
panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
rtt-target = { version = "0.3", features = ["cortex-m"] }
cortex-m = "0.7"
usbd-serial = "0.1"
cortex-m-rtic = "1.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 atsame51j support
default = ["rt", "atsamd-hal/same51j", "atsamd-hal/same51"]
rt = ["cortex-m-rt", "atsamd-hal/same51j-rt"]
unproven = ["atsamd-hal/unproven"]
usb = ["atsamd-hal/usb", "usb-device"]
can = ["atsamd-hal/can"]
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"

[[example]]
name = "pwm"
required-features = ["unproven"]

[[example]]
name = "usb_echo"
required-features = ["usb"]

[[example]]
name = "dmac"
required-features = ["dma"]

[[example]]
name = "uart"
required-features = ["dma"]

[[example]]
name = "pukcc_test"
required-features = ["unproven", "usb"]

[[example]]
name = "nvm_dsu"
required-features = ["unproven", "usb"]

[[example]]
name = "smart_eeprom"
required-features = ["unproven", "usb"]

[[example]]
name = "i2c"
required-features = ["atsamd-hal/dma"]

[[example]]
name = "mcan"
required-features = ["can"]
26 changes: 26 additions & 0 deletions boards/feather_m4_can/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Adafruit Feather M4 Board Support Crate

This crate provides a type-safe API for working with the [Adafruit Feather M4
board](https://www.adafruit.com/product/3857).

## 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_can`
* 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_can/target/thumbv7em-none-eabihf/release/examples/blinky_basic"
Finished in 0.079s
$
```
16 changes: 16 additions & 0 deletions boards/feather_m4_can/build.rs
Original file line number Diff line number Diff line change
@@ -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");
}
37 changes: 37 additions & 0 deletions boards/feather_m4_can/examples/blinky_basic.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
171 changes: 171 additions & 0 deletions boards/feather_m4_can/examples/clocking_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#![no_main]
#![no_std]

use panic_halt as _;

use core::fmt::Write as _;

use atsamd_hal::{
clock::v2::{
self as clock,
dpll::Dpll,
gclk::{Gclk, GclkDiv16, GclkDiv8},
osculp32k::OscUlp32k,
pclk::Pclk,
rtcosc::RtcOsc,
xosc32k::{ControlGainMode, Xosc1k, Xosc32k, Xosc32kBase},
},
ehal::serial::Read as _,
ehal::serial::Write,
gpio::{Pins, PA04, PA05},
rtc::{ClockMode, Rtc},
sercom::{
uart::{self, BaudMode, Flags, Oversampling},
IoSet3, Sercom0,
},
time::U32Ext,
};

use rtic::app;

type Pads = uart::PadsFromIds<Sercom0, IoSet3, PA05, PA04>;
type Uart = uart::Uart<uart::Config<Pads>, uart::Duplex>;

#[app(device = atsamd_hal::pac, peripherals = true)]
mod app {
use super::*;

#[shared]
struct SharedResources {
uart: Uart,
rtc: Rtc<ClockMode>,
}

#[local]
struct LocalResources {}

#[init]
fn init(cx: init::Context) -> (SharedResources, LocalResources, init::Monotonics()) {
let mut device = cx.device;

// Get the clocks & tokens
let (_buses, clocks, tokens) = clock::clock_system_at_reset(
device.OSCCTRL,
device.OSC32KCTRL,
device.GCLK,
device.MCLK,
&mut device.NVMCTRL,
);

// This is required because the `sercom` and `rtc` modules have not yet
// been update to use `clock::v2`
let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() };

// Get the pins
let pins = Pins::new(device.PORT);

// Take `Dfll` 48 MHz, divide down to `2 MHz` through `Gclk1`
let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();

// Output `Gclk1` on PB15 pin
let (gclk1, _gclk1_out) = gclk1.enable_gclk_out(pins.pb15);

// Setup a peripheral channel to power up `Dpll0` from `Gclk1`
let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1);

// Configure `Dpll0` with `2 * 60 + 0/32 = 120 MHz` frequency
let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0)
.loop_div(60, 0)
.enable();

// Swap source of `Gclk0` from Dfll to Dpll0, `48 Mhz -> 120 MHz`
let (gclk0, _dfll, _dpll0) = clocks.gclk0.swap_sources(dfll, dpll0);

// Output `Gclk0` on pin PB14
let (gclk0, _gclk0_out) = gclk0.enable_gclk_out(pins.pb14);

// Setup a peripheral channel to power up `Dpll1` from `Gclk1`
let (pclk_dpll1, _gclk1) = Pclk::enable(tokens.pclks.dpll1, gclk1);

// Configure `Dpll1` with `2 * 50 + 0/32 = 100 MHz` frequency
let dpll1 = Dpll::from_pclk(tokens.dpll1, pclk_dpll1)
.loop_div(50, 0)
.enable();

// Output `Dpll1` on PB20 pin via `Gclk6`, divided by 200 resulting in 0.5 MHz
// output frequency
let (gclk6, _dpll1) = Gclk::from_source(tokens.gclks.gclk6, dpll1);
let gclk6 = gclk6.div(GclkDiv8::Div(200)).enable();
let (_gclk6, _gclk6_out) = gclk6.enable_gclk_out(pins.pb12);

// Configure `Xosc32k` with both outputs (1kHz, 32kHz) activated
let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01)
.control_gain_mode(ControlGainMode::HighSpeed)
.on_demand(false)
.run_standby(true)
.enable();
let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base);
let (xosc32k, _xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base);

// Output `Xosc32k` on PB16 pin via `Gclk2`, divided by 2 resulting in 16 kHz
// output frequency
let (gclk2, _xosc32k) = Gclk::from_source(tokens.gclks.gclk2, xosc32k);
let gclk2 = gclk2.div(GclkDiv8::Div(2)).enable();
let (_gclk2, _gclk2_out) = gclk2.enable_gclk_out(pins.pb16);

// Output `OscUlp32k` on PB11 pin via `Gclk5`, without any division resulting in
// 32 kHz output frequency
let (osculp32k, _osculp_base) =
OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base);
let (gclk5, _osculp32k) = Gclk::from_source(tokens.gclks.gclk5, osculp32k);
let gclk5 = gclk5.enable();
let (_gclk5, _gclk5_out) = gclk5.enable_gclk_out(pins.pb11);

// Setup a peripheral channel to power up `Uart` from `Gclk0`
let (pclk_sercom0, _gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0);

use atsamd_hal::sercom::uart;

let pads = uart::Pads::default().rx(pins.pa05).tx(pins.pa04);
// In the future, the `Uart` will take ownership of the `Pclk` and will
// take an `ApbClk` instead of `&MCLK`
let mut uart = uart::Config::new(&mclk, device.SERCOM0, pads, pclk_sercom0.freq())
.baud(115_200.hz(), BaudMode::Arithmetic(Oversampling::Bits16))
.enable();
uart.enable_interrupts(Flags::RXC);

// Initialize the RTC oscillator from the 1 kHz output of XOSC32K
let (rtc_osc, _xosc1k) = RtcOsc::enable(tokens.rtcosc, xosc1k);

// Setup an `Rtc` in `ClockMode`
// In the future, the `Rtc` will take ownership of the `RtcOsc`
let rtc = Rtc::clock_mode(device.RTC, rtc_osc.freq(), &mut mclk);

writeln!(&mut uart as &mut dyn Write<_, Error = _>, "RTIC booted!").unwrap();

(
SharedResources { uart, rtc },
LocalResources {},
init::Monotonics(),
)
}

#[task(binds = SERCOM0_2, shared = [uart, rtc])]
fn uart(cx: uart::Context) {
let mut uart = cx.shared.uart;
let mut rtc = cx.shared.rtc;
// Read from `Uart` to clean interrupt flag
let _ = uart.lock(|u| u.read().unwrap());

// Print out `DateTime` coming from `Rtc`
uart.lock(|u| {
writeln!(
u as &mut dyn Write<_, Error = _>,
"{:#?}",
rtc.lock(|r| r.current_time())
)
.unwrap()
});
}
}
Loading

0 comments on commit 5cb07f8

Please sign in to comment.