rktk_drivers_common/
panic_utils.rs1use core::{fmt::Write, mem::MaybeUninit, ptr::write_volatile};
10
11use embassy_time::Timer;
12use embedded_graphics::{
13 mono_font::{MonoFont, MonoTextStyle, ascii::FONT_8X13},
14 pixelcolor::BinaryColor,
15 prelude::*,
16 text::{Baseline, Text},
17};
18use rktk::{
19 drivers::interface::display::DisplayDriver,
20 task::display::utils::{RotatedDrawTarget, Rotation},
21};
22
23#[unsafe(link_section = ".uninit.PANICINFO")]
24static mut PANIC_INFO: MaybeUninit<PanicMessage> = MaybeUninit::uninit();
25const PANIC_INFO_MAGIC: u32 = 0x54_41_43_4B;
26
27#[repr(C)]
28pub struct PanicMessage {
29 magic: u32,
30 len: u16,
31 data: [u8; 1024],
32}
33
34impl Write for PanicMessage {
35 fn write_str(&mut self, s: &str) -> core::fmt::Result {
36 let writable_str = if s.len() + self.len as usize > self.data.len() {
37 let writable_len = self.data.len() - self.len as usize;
38 if writable_len == 0 {
39 return Ok(());
40 }
41 s.get(..writable_len).unwrap_or(s)
42 } else {
43 s
44 };
45
46 let new_len = writable_str.len() as u16 + self.len;
47 self.data[self.len as usize..new_len as usize].copy_from_slice(writable_str.as_bytes());
48 self.len = new_len;
49
50 Ok(())
51 }
52}
53
54impl Default for PanicMessage {
55 fn default() -> Self {
56 Self {
57 magic: PANIC_INFO_MAGIC,
58 len: 0,
59 data: [0; 1024],
60 }
61 }
62}
63
64impl PanicMessage {
65 fn reset() -> Self {
66 Self {
67 magic: 0,
68 len: 0,
69 data: [0; 1024],
70 }
71 }
72}
73
74pub fn save_panic_info(info: &core::panic::PanicInfo) {
78 let mut panic_info = PanicMessage::default();
79 write!(panic_info, "{}", info).ok();
80
81 unsafe {
82 write_volatile(&raw mut PANIC_INFO, MaybeUninit::new(panic_info));
83 }
84}
85
86fn read_panic_message() -> Option<PanicMessage> {
87 unsafe {
88 let info = core::ptr::read(&raw const PANIC_INFO);
89 let info = info.assume_init();
90 if info.magic == PANIC_INFO_MAGIC {
91 write_volatile(&raw mut PANIC_INFO, MaybeUninit::new(PanicMessage::reset()));
92 Some(info)
93 } else {
94 None
95 }
96 }
97}
98
99fn parse_panic_message(panic_info: &PanicMessage) -> &str {
100 match core::str::from_utf8(&panic_info.data[..panic_info.len as usize]) {
104 Ok(str) => str,
105 Err(e) => {
106 let valid_len = e.valid_up_to();
107 core::str::from_utf8(&panic_info.data[..valid_len]).unwrap()
108 }
109 }
110}
111
112const FONT: MonoFont = FONT_8X13;
113
114pub async fn display_message_if_panicked<D: DisplayDriver>(display: &mut D) {
122 if let Some(panic_info) = read_panic_message() {
123 if display.init().await.is_ok() {
124 let char_width = FONT.character_size.width as usize;
125
126 let str = parse_panic_message(&panic_info);
127
128 rktk_log::error!("Previous panic detected: {:?}", str);
129
130 let str_len = str
131 .lines()
132 .map(|line| line.chars().count())
133 .max()
134 .unwrap_or(0) as usize;
135
136 let orig_display_size = display.as_mut().bounding_box().size;
137 let rotation = if orig_display_size.width > orig_display_size.height {
138 Rotation::Rotate0
139 } else {
140 Rotation::Rotate90
141 };
142
143 let rotated_display = RotatedDrawTarget::new(display.as_mut(), rotation);
144 let display_width = rotated_display.bounding_box().size.width as usize;
145 let overflow_len = if str_len * char_width > display_width {
146 str_len - display_width / char_width + 1
147 } else {
148 0
149 };
150
151 display_mes(display, "Panic!", Point::zero(), rotation).await;
152 Timer::after_millis(400).await;
153
154 if overflow_len > 0 {
155 loop {
156 for i in 0..=overflow_len {
157 display_mes(
158 display,
159 str,
160 Point::new(-((i * char_width) as i32), 0),
161 rotation,
162 )
163 .await;
164 Timer::after_millis(200).await;
165 }
166 }
167 } else {
168 display_mes(display, str, Point::zero(), rotation).await;
169 Timer::after_secs(100000000).await;
170 }
171 }
172 }
173}
174pub async fn display_mes<D: DisplayDriver>(
175 display: &mut D,
176 str: &str,
177 pos: Point,
178 rotation: Rotation,
179) {
180 {
181 let mut rotated_display = RotatedDrawTarget::new(display.as_mut(), rotation);
182
183 let _ = rotated_display.clear(BinaryColor::Off);
184 let _ = Text::with_baseline(
185 str,
186 pos,
187 MonoTextStyle::new(&FONT, BinaryColor::On),
188 Baseline::Top,
189 )
190 .draw(&mut rotated_display);
191 }
192 let _ = display.flush().await;
193}