teensy4_panic/lib.rs
1//! Panic handler for the Teensy 4.
2//!
3//! When you link `teensy4-panic` into your program, any `panic!()` will cause
4//! your Teensy's LED to blink S.O.S. in Morse code. Supports both Teensy 4.0 and
5//! 4.1 boards.
6//!
7//! # Usage
8//!
9//! Depend on `teensy4-panic`:
10//!
11//! ```toml
12//! [dependencies]
13//! teensy4-panic = "0.2"
14//! ```
15//!
16//! Then, include the crate in your final program:
17//!
18//! ```rust
19//! use teensy4_panic as _;
20//! ```
21//!
22//! Finally, use `panic!()` to stop the program and blink the LED.
23//!
24//! # Features
25//!
26//! The table below summarizes this crate's features. Each subsection details the feature.
27//!
28//! | Feature | Description | Default feature? |
29//! | --------------- | -------------------------------------------------- | ---------------- |
30//! | `panic-handler` | Define the Teensy 4's panic handler in this crate | ✓ |
31//! | `log` | Log the panic message using `log::error!` | |
32//!
33//! It does not make sense to _disable_ `panic-handler` and _enable_ `log`, since logging can
34//! only happen when `panic-handler` is enabled.
35//!
36//! ## Custom panic handlers
37//!
38//! By default, `teensy4-panic` enables the `panic-handler` feature. If you want
39//! to use the S.O.S. routine in your own panic handler, disable the default
40//! features, and call `sos()`:
41//!
42//! ```toml
43//! [dependencies]
44//! teensy4-panic = "0.2"
45//! default-features = false
46//! ```
47//!
48//! ```no_run
49//! # #![feature(lang_items)]
50//! # #![no_std]
51//! use teensy4_panic::sos;
52//!
53//! #[panic_handler]
54//! fn panic(_: &core::panic::PanicInfo) -> ! {
55//! // Your panic handler here...
56//! sos()
57//! }
58//! # fn main() {}
59//! # #[lang = "eh_personality"] extern fn rust_eh_personality() {}
60//! ```
61//!
62//! ## Log the panic message
63//!
64//! If the `log` feature is enabled, the crate links with [the `log` crate](https://crates.io/crates/log).
65//! Before blinking, the panic handler logs the panic message and source location at the error priority,
66//! using `log::error!`. The logging target is `teensy4_panic`.
67//!
68//! The example below shows a `panic!` and an example of its corresponding log message:
69//!
70//! ```no_run
71//! const DELAY_MS: u32 = 5_000;
72//! panic!("This is a panic message written after {}ms", DELAY_MS);
73//! ```
74//! ```text
75//! [ERROR teensy4_panic]: panicked at 'This is a panic message written after 5000ms', examples/panic_log.rs:22:5
76//! ```
77//!
78//! The panic handler only emits the log message once. You're responsible for making sure that the
79//! log message can reach its destination while a `panic!` is active.
80
81#![no_std]
82
83/// The LED
84struct Led();
85
86const GPIO2_BASE: u32 = 0x401BC000;
87
88impl Led {
89 /// Construct the LED
90 ///
91 /// When `new` returns, the LED will be ready to set, clear, and toggle.
92 ///
93 /// # Safety
94 ///
95 /// There should only be one LED in the program. This will modify peripheral memory
96 /// to enable the LED.
97 unsafe fn new() -> Self {
98 const IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03: *mut u32 = 0x401F_8148 as *mut u32;
99 const IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03: *mut u32 = 0x401F_8338 as *mut u32;
100 const IOMUXC_GPR_GPR27: *mut u32 = 0x400A_C06C as *mut u32;
101
102 const fn drive_strength_enable(dse: u32) -> u32 {
103 (dse & 0x07) << 3
104 }
105
106 const GPIO2_GDIR: *mut u32 = (GPIO2_BASE + 0x04) as *mut u32;
107
108 // Set the LED pad into Alt5
109 IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03.write_volatile(5);
110 // Increase drive strength, clearing all other fields
111 IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03.write_volatile(drive_strength_enable(7));
112 // Disable fast mode so that GPIO2 registers drive the pad
113 IOMUXC_GPR_GPR27.write_volatile(IOMUXC_GPR_GPR27.read_volatile() & !(1 << 3));
114 GPIO2_GDIR.write_volatile(GPIO2_GDIR.read_volatile() | (1 << 3));
115
116 Led()
117 }
118
119 /// Drive the LED high
120 fn set(&mut self) {
121 const GPIO2_DR_SET: *mut u32 = (GPIO2_BASE + 0x84) as *mut u32;
122 unsafe { GPIO2_DR_SET.write_volatile(1 << 3) };
123 }
124
125 /// Drive the LED low
126 fn clear(&mut self) {
127 const GPIO2_DR_CLEAR: *mut u32 = (GPIO2_BASE + 0x88) as *mut u32;
128 unsafe { GPIO2_DR_CLEAR.write_volatile(1 << 3) };
129 }
130}
131
132/// Assign all DelayTicks to a `const`. If the factor overflows, it's
133/// caught at compile time. Use this to explore different delays in
134/// debug builds.
135#[derive(Clone, Copy)]
136struct DelayTicks(u32);
137impl DelayTicks {
138 const fn new(factor: u32) -> Self {
139 Self(80_000_000 * factor)
140 }
141}
142
143fn delay(_ticks: DelayTicks) {
144 #[cfg(all(target_arch = "arm", target_os = "none"))]
145 unsafe {
146 core::arch::asm! {
147 r#"
148 5:
149 subs {ticks}, #1 @ ticks--; Z = (0 == ticks);
150 bne 5b @ if (0 == Z) goto 5;
151 "#,
152 ticks = inout(reg) _ticks.0 => _,
153 };
154 }
155}
156
157// Morse timing
158const SOS_DIT: DelayTicks = DelayTicks::new(1);
159const SOS_DAH: DelayTicks = DelayTicks::new(3);
160const SOS_INTRA_CHAR: DelayTicks = DelayTicks::new(1);
161const SOS_INTER_CHAR: DelayTicks = DelayTicks::new(3);
162const SOS_WORD_SPACE: DelayTicks = DelayTicks::new(7);
163
164fn dit(led: &mut Led) {
165 led.set();
166 delay(SOS_DIT);
167 led.clear();
168}
169
170fn dah(led: &mut Led) {
171 led.set();
172 delay(SOS_DAH);
173 led.clear();
174}
175
176fn s(led: &mut Led) {
177 dit(led);
178 delay(SOS_INTRA_CHAR);
179 dit(led);
180 delay(SOS_INTRA_CHAR);
181 dit(led);
182}
183
184fn o(led: &mut Led) {
185 dah(led);
186 delay(SOS_INTRA_CHAR);
187 dah(led);
188 delay(SOS_INTRA_CHAR);
189 dah(led);
190}
191
192#[cfg(feature = "panic-handler")]
193#[panic_handler]
194fn panic(_info: &core::panic::PanicInfo) -> ! {
195 #[cfg(feature = "log")]
196 {
197 use core::sync::atomic::{AtomicBool, Ordering};
198 // If logging results in another panic, we shouldn't try again.
199 static TRY_TO_LOG: AtomicBool = AtomicBool::new(true);
200 if TRY_TO_LOG.fetch_and(false, Ordering::Relaxed) {
201 log::error!("{}", _info);
202 }
203 }
204 sos()
205}
206
207/// Blink S.O.S. on the LED, forever
208///
209/// This is the implementation of the `teensy4-panic` handler.
210/// See the crate-level docs for how you could use this in your
211/// own panic handler.
212pub fn sos() -> ! {
213 let mut led = unsafe { Led::new() };
214 loop {
215 s(&mut led);
216 delay(SOS_INTER_CHAR);
217 o(&mut led);
218 delay(SOS_INTER_CHAR);
219 s(&mut led);
220
221 delay(SOS_WORD_SPACE);
222 }
223}