Skip to content

Commit 3993a0d

Browse files
committed
'fix' for timeout issue on SPIM during display writes
occassionally the hardware block just goes unresponsive. the 'fix' is to reset it, and then re-initialize the interface. I suspect this has to do with the UDMA silently dropping transactions when there's too much contention on the bus.
1 parent b7826c2 commit 3993a0d

File tree

6 files changed

+81
-50
lines changed

6 files changed

+81
-50
lines changed

libs/bao1x-hal/src/sh1107.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ pub struct Oled128x128<'a> {
183183
powerdown: bool,
184184
power_port: IoxPort,
185185
power_pin: u8,
186+
clk_div: u8,
186187
}
187188

188189
impl<'a> Oled128x128<'a> {
@@ -267,9 +268,19 @@ impl<'a> Oled128x128<'a> {
267268
power_port,
268269
power_pin,
269270
powerdown: false,
271+
clk_div: ((perclk_freq / 2) / 2_000_000) as u8,
270272
}
271273
}
272274

275+
// used to recover a wedged SPI interface
276+
pub fn reinit_spi(&mut self) {
277+
self.spim.send_cmd_list(&[crate::udma::SpimCmd::Config(
278+
SpimClkPol::LeadingEdgeRise,
279+
SpimClkPha::CaptureOnLeading,
280+
self.clk_div,
281+
)]);
282+
}
283+
273284
/// This should only be called to initialize the panic handler with its own
274285
/// copy of hardware registers.
275286
///
@@ -300,8 +311,9 @@ impl<'a> Oled128x128<'a> {
300311
Option<CommandSet>,
301312
),
302313
bool,
314+
u8,
303315
) {
304-
(self.spim.into_raw_parts(), self.powerdown)
316+
(self.spim.into_raw_parts(), self.powerdown, self.clk_div)
305317
}
306318

307319
/// Creates a clone of the display handle. This is only safe if the handles are used in a
@@ -331,6 +343,7 @@ impl<'a> Oled128x128<'a> {
331343
Option<CommandSet>,
332344
),
333345
bool,
346+
u8,
334347
),
335348
iox: &'a T,
336349
) -> Self
@@ -354,6 +367,7 @@ impl<'a> Oled128x128<'a> {
354367
command_set,
355368
),
356369
powerdown,
370+
clk_div,
357371
) = display_parts;
358372
// compile them into a new object
359373
let mut spim = unsafe {
@@ -398,6 +412,7 @@ impl<'a> Oled128x128<'a> {
398412
powerdown,
399413
power_pin,
400414
power_port,
415+
clk_div,
401416
}
402417
}
403418

@@ -407,7 +422,7 @@ impl<'a> Oled128x128<'a> {
407422

408423
pub fn screen_size(&self) -> Point { Point::new(WIDTH, LINES) }
409424

410-
pub fn redraw(&mut self) { self.draw(); }
425+
pub fn redraw(&mut self) -> Result<(), xous::Error> { self.draw() }
411426

412427
pub fn blit_screen(&mut self, bmp: &[u32]) { self.buffer.copy_from_slice(bmp); }
413428

@@ -417,21 +432,21 @@ impl<'a> Oled128x128<'a> {
417432

418433
pub fn stash(&mut self) { self.stash.copy_from_slice(&self.buffer); }
419434

420-
pub fn pop(&mut self) {
435+
pub fn pop(&mut self) -> Result<(), xous::Error> {
421436
self.buffer.copy_from_slice(&self.stash);
422-
self.redraw();
437+
self.redraw()
423438
}
424439

425440
fn set_data(&self) { self.iox.set_gpio_pin_value(self.cd_port, self.cd_pin, IoxValue::High); }
426441

427442
fn set_command(&self) { self.iox.set_gpio_pin_value(self.cd_port, self.cd_pin, IoxValue::Low); }
428443

429-
pub fn send_command<'b, U>(&'b mut self, cmd: U)
444+
pub fn send_command<'b, U>(&'b mut self, cmd: U) -> Result<(), xous::Error>
430445
where
431446
U: IntoIterator<Item = u8> + 'b,
432447
{
433448
if self.powerdown {
434-
return;
449+
return Ok(());
435450
}
436451
self.set_command();
437452
let total_buf_len = self.buffer.len() * size_of::<u32>();
@@ -451,16 +466,12 @@ impl<'a> Oled128x128<'a> {
451466
.txrx_data_async_from_parts::<u8>(total_buf_len, len, true, false)
452467
.expect("Couldn't initiate oled command");
453468
}
454-
self.spim.txrx_await(false).unwrap_or_else(|_e| {
455-
#[cfg(feature = "std")]
456-
log::error!("txrx err {:?}", _e);
457-
&[]
458-
});
469+
self.spim.txrx_await(false).map(|_| ())
459470
}
460471

461-
pub fn init(&mut self) {
472+
pub fn init(&mut self) -> Result<(), xous::Error> {
462473
if self.powerdown {
463-
return;
474+
return Ok(());
464475
}
465476
use Command::*;
466477
let init_sequence = [
@@ -483,17 +494,18 @@ impl<'a> Oled128x128<'a> {
483494

484495
for command in init_sequence {
485496
let bytes = command.encode();
486-
self.send_command(bytes);
497+
self.send_command(bytes)?;
487498
}
488499
// clear the frame buffer
489500
self.buffer_mut().fill(0xFFFF_FFFF);
490-
self.draw();
501+
self.draw()?;
491502

492503
let display_on = [DisplayOnOff(DisplayState::On)];
493504
for command in display_on {
494505
let bytes = command.encode();
495-
self.send_command(bytes);
506+
self.send_command(bytes)?;
496507
}
508+
Ok(())
497509
}
498510

499511
pub fn powerdown(&mut self) {
@@ -516,9 +528,9 @@ impl<'a> Oled128x128<'a> {
516528
self.powerdown = false;
517529
}
518530

519-
pub fn brightness(&mut self, level: u8) {
531+
pub fn brightness(&mut self, level: u8) -> Result<(), xous::Error> {
520532
let bytes = Command::SetContrastControl(level).encode();
521-
self.send_command(bytes);
533+
self.send_command(bytes)
522534
}
523535

524536
#[cfg(feature = "std")]
@@ -574,10 +586,10 @@ impl<'a> Oled128x128<'a> {
574586

575587
impl<'a> FrameBuffer for Oled128x128<'a> {
576588
/// Copies the SRAM buffer to IFRAM and then transfers that over SPI
577-
fn draw(&mut self) {
589+
fn draw(&mut self) -> Result<(), xous::Error> {
578590
self.hw_buf.copy_from_slice(&self.buffer);
579591
if self.powerdown {
580-
return;
592+
return Ok(());
581593
}
582594
let chunk_size = 16;
583595
let chunks = self.buffer().len() * size_of::<u32>() / chunk_size;
@@ -588,8 +600,8 @@ impl<'a> FrameBuffer for Oled128x128<'a> {
588600
// the transaction is done before the data is done transmitting, and we have to
589601
// toggle set_data() only after the physical transaction is done, not after the
590602
// the last UDMA action has been queued.
591-
self.send_command(Command::SetPageAddress(0).encode());
592-
self.send_command(Command::SetColumnAddress(page as u8).encode());
603+
self.send_command(Command::SetPageAddress(0).encode())?;
604+
self.send_command(Command::SetColumnAddress(page as u8).encode())?;
593605
// wait for commands to finish before toggling set_data
594606
// self.spim.tx_data_await(false);
595607
// crate::println!("Send page {}, offset {:x}", page, page * chunk_size);
@@ -600,12 +612,9 @@ impl<'a> FrameBuffer for Oled128x128<'a> {
600612
.txrx_data_async_from_parts::<u8>(page * chunk_size, chunk_size, true, false)
601613
.expect("Couldn't initiate oled data transfer");
602614
}
603-
self.spim.txrx_await(false).unwrap_or_else(|_e| {
604-
#[cfg(feature = "std")]
605-
log::error!("txrx err {:?}", _e);
606-
&[]
607-
});
615+
self.spim.txrx_await(false)?;
608616
}
617+
Ok(())
609618
}
610619

611620
fn clear(&mut self) { self.buffer_mut().fill(0xFFFF_FFFF); }

libs/bao1x-hal/src/udma/spim.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ impl Spim {
476476
&self.ifram.as_phys_slice()[..self.tx_buf_len_bytes / size_of::<T>()]
477477
}
478478

479-
fn send_cmd_list(&mut self, cmds: &[SpimCmd]) {
479+
pub fn send_cmd_list(&mut self, cmds: &[SpimCmd]) {
480480
for cmd_chunk in cmds.chunks(SPIM_CMD_BUF_LEN_BYTES / size_of::<u32>()) {
481481
for (src, dst) in cmd_chunk.iter().zip(self.cmd_buf_mut().iter_mut()) {
482482
*dst = (*src).into();

libs/ux-api/src/minigfx/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub trait FrameBuffer {
4747
/// and background colors for a color theme.
4848
fn xor_pixel(&mut self, p: Point);
4949
/// Swaps the drawable buffer to the screen and sends it to the hardware
50-
fn draw(&mut self);
50+
fn draw(&mut self) -> Result<(), xous::Error>;
5151
/// Clears the drawable buffer
5252
fn clear(&mut self);
5353
/// Returns the size of the frame buffer as a Point
@@ -68,7 +68,7 @@ impl FrameBuffer for DynFb<'_> {
6868

6969
fn get_pixel(&self, p: Point) -> Option<ColorNative> { self.0.get_pixel(p) }
7070

71-
fn draw(&mut self) { self.0.draw(); }
71+
fn draw(&mut self) -> Result<(), xous::Error> { self.0.draw() }
7272

7373
fn clear(&mut self) { self.0.clear(); }
7474

loader/src/platform/bao1x/bao1x.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ pub fn early_init_hw() -> u32 {
168168
&mut iox,
169169
&mut udma_global,
170170
);
171-
sh1107.init();
171+
sh1107.init().ok();
172172
sh1107.buffer_mut().fill(0xFFFF_FFFF);
173173
sh1107.blit_screen(&ux_api::bitmaps::baochip128x128::BITMAP);
174-
sh1107.draw();
174+
sh1107.draw().ok();
175175
}
176176

177177
#[cfg(feature = "board-dabao")]
@@ -228,10 +228,10 @@ pub fn oled_init<'a>(
228228
iox,
229229
udma_global,
230230
);
231-
sh1107.init();
231+
sh1107.init().ok();
232232
sh1107.buffer_mut().fill(0xFFFF_FFFF);
233233
sh1107.blit_screen(&ux_api::bitmaps::baochip128x128::BITMAP);
234-
sh1107.draw();
234+
sh1107.draw().ok();
235235
sh1107
236236
}
237237

@@ -415,5 +415,5 @@ pub fn progress_bar(fb: &mut dyn FrameBuffer, progress: usize) {
415415
false,
416416
);
417417
}
418-
fb.0.draw();
418+
fb.0.draw().ok();
419419
}

services/bao-video/src/main.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,10 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
227227
let hal = Hal::new();
228228

229229
let mut display = Oled128x128::new(main_thread_token, bao1x_api::PERCLK, &iox, &udma_global);
230-
display.init();
230+
// these unwrap because if we have a time-out at this stage, it's likely a hardware problem
231+
display.init().unwrap();
231232
display.clear();
232-
display.draw();
233+
display.draw().unwrap();
233234

234235
// ---- panic handler - set up early so we can see panics quickly
235236
// install the graphical panic handler. It won't catch really early panics, or panics in this crate,
@@ -522,7 +523,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
522523
)
523524
};
524525
response.replace(acquisition).unwrap();
525-
display.pop();
526+
display
527+
.pop()
528+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
526529
hal.set_preemption(true);
527530
}
528531
// forward messages on to listeners iff we don't have an active modal
@@ -603,7 +606,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
603606
Mono::White.into(),
604607
Mono::Black.into(),
605608
);
606-
display.draw();
609+
display
610+
.draw()
611+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
607612
let mut img =
608613
rqrr::PreparedImage::prepare_from_greyscale(IMAGE_WIDTH, IMAGE_HEIGHT, |x, y| {
609614
frame[y * IMAGE_WIDTH + x]
@@ -641,7 +646,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
641646
)
642647
};
643648
response.replace(acquisition).unwrap();
644-
display.pop();
649+
display.pop().unwrap_or_else(|_| {
650+
display_timeout_handler(&udma_global, &mut display)
651+
});
645652
#[cfg(not(feature = "hosted-baosec"))]
646653
hal.set_preemption(true);
647654
} else {
@@ -685,7 +692,7 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
685692
);
686693
}
687694

688-
display.draw();
695+
display.draw().unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
689696

690697
// clear the front buffer
691698
display.clear();
@@ -762,7 +769,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
762769
GfxOpcode::Flush => {
763770
if qr_request.is_none() {
764771
log::trace!("***gfx flush*** redraw##");
765-
display.redraw();
772+
display
773+
.redraw()
774+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
766775
}
767776
}
768777
GfxOpcode::Clear => {
@@ -797,15 +806,19 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
797806
GfxOpcode::DrawSleepScreen => {
798807
if let Some(_scalar) = msg.body.scalar_message() {
799808
display.blit_screen(&ux_api::bitmaps::baochip128x128::BITMAP);
800-
display.redraw();
809+
display
810+
.redraw()
811+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
801812
} else {
802813
panic!("Incorrect message type");
803814
}
804815
}
805816
GfxOpcode::DrawBootLogo => {
806817
if let Some(_scalar) = msg.body.scalar_message() {
807818
display.blit_screen(&ux_api::bitmaps::baochip128x128::BITMAP);
808-
display.redraw();
819+
display
820+
.redraw()
821+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
809822
} else {
810823
panic!("Incorrect message type");
811824
}
@@ -833,7 +846,7 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
833846
// no failure if it's not
834847
}
835848
GfxOpcode::Pop => {
836-
display.pop();
849+
display.pop().unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
837850
if let Some(scalar) = msg.body.scalar_message_mut() {
838851
// ack the message if it's a blocking scalar
839852
scalar.arg1 = 1;
@@ -857,8 +870,8 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
857870
// safety: this is safe because we call init() a prescribed delay after power-up
858871
unsafe { display.powerup() };
859872
tt.sleep_ms(5).ok();
860-
display.init();
861-
display.pop();
873+
display.init().unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
874+
display.pop().unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
862875
if let Some(scalar) = msg.body.scalar_message_mut() {
863876
// ack the message if it's a blocking scalar
864877
scalar.arg1 = 1;
@@ -874,7 +887,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
874887
GfxOpcode::Brightness => {
875888
if let Some(scalar) = msg.body.scalar_message_mut() {
876889
let brightness = scalar.arg1.min(255) as u8;
877-
display.brightness(brightness);
890+
display
891+
.brightness(brightness)
892+
.unwrap_or_else(|_| display_timeout_handler(&udma_global, &mut display));
878893
}
879894
}
880895
GfxOpcode::Quit => break,
@@ -894,3 +909,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! {
894909
log::trace!("quitting");
895910
xous::terminate_process(0)
896911
}
912+
913+
fn display_timeout_handler(udma_global: &UdmaGlobal, display: &mut Oled128x128) {
914+
log::info!("resetting display spim block");
915+
udma_global.reset(PeriphId::from(bao1x_hal::board::get_display_pins().0));
916+
display.reinit_spi();
917+
}

0 commit comments

Comments
 (0)