forked from mciantyre/teensy4-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request mciantyre#64 from mitchmindtree/rtic-example
Add Peripherals::steal behind `rtic` feature. Add `rtic-led.rs`, `rtic-blink.rs` and `rtic-uart-log.rs` examples.
- Loading branch information
Showing
6 changed files
with
328 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters