Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Peripherals::steal behind rtic feature. Add rtic-led.rs, rtic-blink.rs and rtic-uart-log.rs examples. #64

Merged
merged 6 commits into from
Jul 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --verbose --workspace --examples --target thumbv7em-none-eabihf -- -D warnings
args: --verbose --all-features --workspace --examples --target thumbv7em-none-eabihf -- -D warnings
name: Run clippy

precompiled:
Expand All @@ -46,4 +46,4 @@ jobs:
- name: Build runtime support
run: INSTALL_DEPS=0 make libt4boot
- name: Build USB stack
run: INSTALL_DEPS=0 make libt4usb
run: INSTALL_DEPS=0 make libt4usb
33 changes: 33 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,27 @@ path = "teensy4-usb-sys"
optional = true

[dev-dependencies]
cortex-m-rtic = "0.5.3"
embedded-hal = "0.2.4"
heapless = "0.5.5"
log = "0.4.8"
imxrt-uart-log = "0.1.0"
nb = "0.1.2"
panic-halt = "0.2.0"

[[example]]
name = "rtic_led"
path = "examples/rtic_led.rs"
required-features = ["rtic"]
[[example]]
name = "rtic_blink"
path = "examples/rtic_blink.rs"
required-features = ["rtic"]
[[example]]
name = "rtic_uart_log"
path = "examples/rtic_uart_log.rs"
required-features = ["rtic"]

[workspace]
members = [
"cortex-m-rt-patch",
Expand All @@ -59,7 +76,16 @@ default = ["usb-logging", "systick"]
usb-logging = ["systick", "teensy4-usb-sys", "log"]
# Include a definition of the SysTick exception handler. This enables
# a simple delay() spinloop that waits for the timer to elapse.
#
# NOTE: This feature is incompatible with the `rtic` crate as `rtic`
# provides its own `SysTick` definition.
systick = ["embedded-hal"]
# Provides the `Peripherals::steal` constructor required by `rtic`.
#
# NOTE: When using this feature along with the `rtic` crate the
# default features must first be disabled in order to avoid a
# duplicate definition of `SysTick`.
rtic = []

# Don't optimize build dependencies, like proc macros.
# Helps with build times.
Expand All @@ -70,3 +96,10 @@ opt-level = 0
# https://github.com/mciantyre/teensy4-rs#runtime
[patch.crates-io.cortex-m-rt]
path = "cortex-m-rt-patch"

# Patch `imxrt-hal` so that we may access the `hal::Peripherals::steal`
# constructor. This patch should be removed once a new version of
# `imxrt-hal` is published with the new constructor.
[patch.crates-io.imxrt-hal]
git = "https://github.com/imxrt-rs/imxrt-rs"
branch = "master"
87 changes: 87 additions & 0 deletions examples/rtic_blink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! An adaptation of the `rtic_led.rs` example that demonstrates:
//!
//! 1. how to share late resources and
//! 2. how to use the systick interrupt to cause the LED to blink.
//!
//! Please refer to the [RTIC book](https://rtic.rs) for more information on RTIC.
//!
//! NOTE: This example requires the `rtic` feature to be enabled.

#![no_std]
#![no_main]

use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use panic_halt as _;
use rtic::cyccnt::U32Ext;
use teensy4_bsp as bsp;

// The CYCCNT counts in clock cycles. Using the clock hz should give us a ~1 second period.
const PERIOD: u32 = bsp::hal::ccm::PLL1::ARM_HZ;

#[rtic::app(device = teensy4_bsp, monotonic = rtic::cyccnt::CYCCNT, peripherals = true)]
const APP: () = {
struct Resources {
led: bsp::LED,
}

#[init(schedule = [blink])]
fn init(mut cx: init::Context) -> init::LateResources {
init_delay();

// Set the clock mode to 'RUN'
//
// i.MX RT (106x) processors will not wake on SYSTICK. When we enter
// WFI or WFE, we'll enter WAIT mode by default. This will disable
// SYSTICK. So, if you're waiting for SYSTICK to wake you up, it won't
// happen. By setting ourselves to 'RUN' low-power mode, SYSTICK will
// still wake us up.
//
// See the CCM_CLPCR register for more information. The HAL docs also note
// this aspect of the processor.
cx.device.ccm.set_mode(bsp::hal::ccm::ClockMode::Run);

// Initialise the monotonic CYCCNT timer.
cx.core.DWT.enable_cycle_counter();

// Ensure the ARM clock is configured for the default speed seeing as we use this speed to
// determine a 1 second `PERIOD`.
cx.device.ccm.pll1.set_arm_clock(
bsp::hal::ccm::PLL1::ARM_HZ,
&mut cx.device.ccm.handle,
&mut cx.device.dcdc,
);

// Schedule the first blink.
cx.schedule.blink(cx.start + PERIOD.cycles()).unwrap();

let mut led = bsp::configure_led(&mut cx.device.gpr, cx.device.pins.p13);
led.set_high().unwrap();

init::LateResources { led }
}

#[task(resources = [led], schedule = [blink])]
fn blink(cx: blink::Context) {
cx.resources.led.toggle().unwrap();
// Schedule the following blink.
cx.schedule.blink(cx.scheduled + PERIOD.cycles()).unwrap();
}

// RTIC requires that unused interrupts are declared in an extern block when
// using software tasks; these free interrupts will be used to dispatch the
// software tasks.
extern "C" {
fn LPUART8();
}
};

// If we reach WFI on teensy 4.0 too quickly it seems to halt. Here we wait a short while in `init`
// to avoid this issue. The issue only appears to occur when rebooting the device (via the button),
// however there appears to be no issue when power cycling the device.
//
// TODO: Investigate exactly why this appears to be necessary.
fn init_delay() {
for _ in 0..10_000_000 {
core::sync::atomic::spin_loop_hint();
}
}
38 changes: 38 additions & 0 deletions examples/rtic_led.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Demonstrates how to use `teensy4_bsp` alongside `rtic`.
//!
//! NOTE: This example requires the `rtic` feature to be enabled.
//!
//! `rtic` stands for "Real-Time Interrupt-driven Concurrency". It is a convenient concurrency
//! framework for building real-time systems. If you are unfamiliar with `rtic`, I recommend
//! reading the online book: https://rtic.rs
//!
//! Success criteria: the LED turns on!

#![no_std]
#![no_main]

extern crate panic_halt;

use embedded_hal::digital::v2::OutputPin;
use teensy4_bsp as bsp;

#[rtic::app(device = teensy4_bsp, peripherals = true)]
const APP: () = {
#[init]
fn init(cx: init::Context) {
// Cortex-M peripherals
let _core: cortex_m::Peripherals = cx.core;

// Device-specific peripherals
let mut device: bsp::Peripherals = cx.device;

let mut led = bsp::configure_led(&mut device.gpr, device.pins.p13);
led.set_high().unwrap();
}
#[idle]
fn idle(_: idle::Context) -> ! {
loop {
core::sync::atomic::spin_loop_hint();
}
}
};
153 changes: 153 additions & 0 deletions examples/rtic_uart_log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! An adaptation of the `rtic_blink.rs` example that demonstrates logging via Teensy 4 UART.
//!
//! This example requires:
//!
//! - The `rtic` feature to be enabled.
//! - a serial to USB converter (tested with CP2102). The converter should be connected to pins 14
//! and 15. Pin 14 is teensy's TX and pin 15 is teensy's RX pin.
//!
//! Success criteria:
//! - The on-board LED should blink once per second.
//! - On each blink, we receive a message from the teensy via the serial console (e.g. `screen`).
//! - When writing serial data from the console, the teensy should log when each call to the
//! interrupt hardware task occurs and prints the characters received as a utf8 string on each
//! blink.

#![no_std]
#![no_main]

use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use embedded_hal::serial::Read;
use heapless::consts::U256;
use panic_halt as _;
use rtic::cyccnt::U32Ext;
use teensy4_bsp as bsp;

const PERIOD: u32 = bsp::hal::ccm::PLL1::ARM_HZ;
const BAUD: u32 = 115_200;
const TX_FIFO_SIZE: u8 = 4;

// Type aliases for the Queue we want to use.
type Ty = u8;
type Cap = U256;
type Queue = heapless::spsc::Queue<Ty, Cap>;
type Producer = heapless::spsc::Producer<'static, Ty, Cap>;
type Consumer = heapless::spsc::Consumer<'static, Ty, Cap>;

// The UART receiver.
type UartRx = bsp::hal::uart::Rx<bsp::hal::iomuxc::uart::module::_2>;

#[rtic::app(device = teensy4_bsp, monotonic = rtic::cyccnt::CYCCNT, peripherals = true)]
const APP: () = {
struct Resources {
led: bsp::LED,
u_rx: UartRx,
q_tx: Producer,
q_rx: Consumer,
}

#[init(schedule = [blink])]
fn init(mut cx: init::Context) -> init::LateResources {
init_delay();

// Setup the clock for rtic scheduling.
cx.device.ccm.set_mode(bsp::hal::ccm::ClockMode::Run);
cx.core.DWT.enable_cycle_counter();
cx.device.ccm.pll1.set_arm_clock(
bsp::hal::ccm::PLL1::ARM_HZ,
&mut cx.device.ccm.handle,
&mut cx.device.dcdc,
);

// UART setup.
let uarts = cx.device.uart.clock(
&mut cx.device.ccm.handle,
bsp::hal::ccm::uart::ClockSelect::OSC,
bsp::hal::ccm::uart::PrescalarSelect::DIVIDE_1,
);
let mut uart = uarts
.uart2
.init(cx.device.pins.p14.alt2(), cx.device.pins.p15.alt2(), BAUD)
.unwrap();
uart.set_tx_fifo(core::num::NonZeroU8::new(TX_FIFO_SIZE));
uart.set_rx_fifo(true);
uart.set_receiver_interrupt(Some(0));
let (u_tx, u_rx) = uart.split();
imxrt_uart_log::blocking::init(u_tx, Default::default()).unwrap();

// The queue used for buffering bytes.
static mut Q: Queue = heapless::spsc::Queue(heapless::i::Queue::new());
let (q_tx, q_rx) = unsafe { Q.split() };

// LED setup.
let mut led = bsp::configure_led(&mut cx.device.gpr, cx.device.pins.p13);
led.set_high().unwrap();

// Schedule the first blink.
cx.schedule.blink(cx.start + PERIOD.cycles()).unwrap();

init::LateResources {
led,
u_rx,
q_tx,
q_rx,
}
}

#[task(resources = [led, q_rx], schedule = [blink])]
fn blink(cx: blink::Context) {
// Log via UART.
static mut TIMES: u32 = 0;
*TIMES += 1;
log::info!(
"`blink` called {} time{}",
*TIMES,
if *TIMES > 1 { "s" } else { "" }
);

// Log all bytes that have been read via UART as a utf8 str.
if cx.resources.q_rx.ready() {
let mut buffer = [0u8; 256];
for elem in buffer.iter_mut() {
*elem = match cx.resources.q_rx.dequeue() {
None => break,
Some(b) => b,
};
}
let s = core::str::from_utf8(&buffer).unwrap();
log::info!("read: {}", s);
}

// Toggle the LED.
cx.resources.led.toggle().unwrap();

// Schedule the following blink.
cx.schedule.blink(cx.scheduled + PERIOD.cycles()).unwrap();
}

#[task(binds = LPUART2, resources = [u_rx, q_tx])]
fn lpuart2(cx: lpuart2::Context) {
log::info!("LPUART2 interrupt task called!");
while let Ok(b) = cx.resources.u_rx.read() {
cx.resources.q_tx.enqueue(b).ok();
}
}

// RTIC requires that unused interrupts are declared in an extern block when
// using software tasks; these free interrupts will be used to dispatch the
// software tasks.
extern "C" {
fn LPUART8();
}
};

// If we reach WFI on teensy 4.0 too quickly it seems to halt. Here we wait a short while in `init`
// to avoid this issue. The issue only appears to occur when rebooting the device (via the button),
// however there appears to be no issue when power cycling the device.
//
// TODO: Investigate exactly why this appears to be necessary.
fn init_delay() {
for _ in 0..10_000_000 {
core::sync::atomic::spin_loop_hint();
}
}
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ pub mod usb;
pub use systick::SysTick;

pub use hal::ral::interrupt;
// `rtic` expects these in the root.
#[cfg(feature = "rtic")]
pub use hal::ral::{interrupt as Interrupt, NVIC_PRIO_BITS};

pub use cortex_m_rt as rt;
pub use imxrt_hal as hal;
Expand Down Expand Up @@ -258,6 +261,18 @@ impl Peripherals {
Some(Peripherals::new(p))
}

#[cfg(feature = "rtic")]
/// Steal all of the HAL's peripherals.
///
/// # Safety
///
/// NOTE: This constructor is only intended for use with the `rtic` crate. This is **not** an
/// alternative to the `take` constructor. The `take` constructor sets the system timer
/// interrupt while this constructor does not seeing as `rtic` will take care of this for us.
pub unsafe fn steal() -> Self {
Self::new(hal::Peripherals::steal())
}
mciantyre marked this conversation as resolved.
Show resolved Hide resolved

fn set_systick(systick: &mut cortex_m::peripheral::SYST) {
systick.disable_counter();
systick.set_clock_source(cortex_m::peripheral::syst::SystClkSource::External);
Expand Down