Skip to content

Commit 0a3bb38

Browse files
Switch to message passing between interrupts and the main loop.
1 parent d707bc2 commit 0a3bb38

File tree

2 files changed

+105
-52
lines changed

2 files changed

+105
-52
lines changed

neotron-bmc-pico/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ defmt-rtt = "0.3"
1313
heapless= "0.7"
1414
panic-probe = { version = "0.3", features = ["print-defmt"] }
1515
stm32f0xx-hal = { version = "0.17", features = ["stm32f030x6", "rt"] }
16-
neotron-bmc-protocol = { path = "../neotron-bmc-protocol" }
16+
neotron-bmc-protocol = { version = "0.1", path = "../neotron-bmc-protocol" }
1717
systick-monotonic = "1.0"
1818
embedded-hal = "*"
1919

neotron-bmc-pico/src/main.rs

Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ mod app {
6161
use super::*;
6262
use systick_monotonic::*; // Implements the `Monotonic` trait
6363

64+
pub enum Message {
65+
/// Word from PS/2 port 0
66+
Ps2Data0(u16),
67+
/// Word from PS/2 port 1
68+
Ps2Data1(u16),
69+
/// Message from SPI bus
70+
SpiRequest(neotron_bmc_protocol::Request),
71+
/// The power button was given a tap
72+
PowerButtonShortPress,
73+
/// The power button was held down
74+
PowerButtonLongPress,
75+
/// The reset button was given a tap
76+
ResetButtonShortPress,
77+
/// The UART got some data
78+
UartByte(u8),
79+
}
80+
6481
#[shared]
6582
struct Shared {
6683
/// The power LED (D1101)
@@ -112,9 +129,11 @@ mod app {
112129
/// Our register state
113130
#[lock_free]
114131
register_state: RegisterState,
115-
/// Keyboard words sink
132+
/// Read messages here
116133
#[lock_free]
117-
kb_q_out: Consumer<'static, u16, 8>,
134+
msg_q_out: Consumer<'static, Message, 8>,
135+
/// Write messages here
136+
msg_q_in: Producer<'static, Message, 8>,
118137
/// SPI Peripheral
119138
spi: neotron_bmc_pico::spi::SpiPeripheral<5, 64>,
120139
/// CS pin
@@ -131,8 +150,6 @@ mod app {
131150
press_button_reset_short: debouncr::Debouncer<u8, debouncr::Repeat2>,
132151
/// Keyboard PS/2 decoder
133152
kb_decoder: neotron_bmc_pico::ps2::Ps2Decoder,
134-
/// Keyboard words source
135-
kb_q_in: Producer<'static, u16, 8>,
136153
}
137154

138155
#[monotonic(binds = SysTick, default = true)]
@@ -144,7 +161,7 @@ mod app {
144161
///
145162
/// * Task `led_power_blink` - blinks the LED
146163
/// * Task `button_poll` - checks the power and reset buttons
147-
#[init(local = [ queue: Queue<u16, 8> = Queue::new()])]
164+
#[init(local = [ queue: Queue<Message, 8> = Queue::new()])]
148165
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
149166
defmt::info!("Neotron BMC version {:?} booting", VERSION);
150167

@@ -285,7 +302,7 @@ mod app {
285302

286303
defmt::info!("Init complete!");
287304

288-
let (kb_q_in, kb_q_out) = ctx.local.queue.split();
305+
let (msg_q_in, msg_q_out) = ctx.local.queue.split();
289306

290307
let shared_resources = Shared {
291308
serial,
@@ -307,7 +324,8 @@ mod app {
307324
firmware_version:
308325
*b"Neotron BMC v0.3.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
309326
},
310-
kb_q_out,
327+
msg_q_out,
328+
msg_q_in,
311329
spi,
312330
pin_cs,
313331
};
@@ -316,7 +334,6 @@ mod app {
316334
press_button_power_long: debouncr::debounce_16(false),
317335
press_button_reset_short: debouncr::debounce_2(false),
318336
kb_decoder: neotron_bmc_pico::ps2::Ps2Decoder::new(),
319-
kb_q_in,
320337
};
321338
let init = init::Monotonics(mono);
322339
(shared_resources, local_resources, init)
@@ -325,45 +342,29 @@ mod app {
325342
/// Our idle task.
326343
///
327344
/// This task is called when there is nothing else to do.
328-
#[idle(shared = [kb_q_out, spi, register_state])]
345+
#[idle(shared = [msg_q_out, msg_q_in, spi, register_state])]
329346
fn idle(mut ctx: idle::Context) -> ! {
330347
defmt::info!("Idle is running...");
331348
loop {
332-
if let Some(word) = ctx.shared.kb_q_out.dequeue() {
333-
if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) {
334-
defmt::info!("< KB 0x{:x}", byte);
335-
} else {
336-
defmt::warn!("< Bad KB 0x{:x}", word);
337-
}
338-
}
339-
340-
let mut req = None;
341-
ctx.shared.spi.lock(|spi| {
342-
let mut mark_done = false;
343-
if let Some(data) = spi.get_received() {
344-
use proto::Receivable;
345-
match proto::Request::from_bytes(data) {
346-
Ok(inner_req) => {
347-
mark_done = true;
348-
req = Some(inner_req);
349-
}
350-
Err(proto::Error::BadLength) => {
351-
// Need more data
352-
}
353-
Err(e) => {
354-
defmt::warn!("Bad Req ({:02x})", e as u8);
355-
mark_done = true;
356-
}
349+
match ctx.shared.msg_q_out.dequeue() {
350+
Some(Message::Ps2Data0(word)) => {
351+
if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) {
352+
defmt::info!("< KB 0x{:x}", byte);
353+
} else {
354+
defmt::warn!("< Bad KB 0x{:x}", word);
357355
}
358356
}
359-
if mark_done {
360-
// Couldn't do this whilst holding the `data` ref.
361-
spi.mark_done();
357+
Some(Message::Ps2Data1(word)) => {
358+
if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) {
359+
defmt::info!("< MS 0x{:x}", byte);
360+
} else {
361+
defmt::warn!("< Bad MS 0x{:x}", word);
362+
}
362363
}
363-
});
364-
365-
match req {
366-
Some(req) => match req.request_type {
364+
Some(Message::PowerButtonLongPress) => {}
365+
Some(Message::PowerButtonShortPress) => {}
366+
Some(Message::ResetButtonShortPress) => {}
367+
Some(Message::SpiRequest(req)) => match req.request_type {
367368
proto::RequestType::Read | proto::RequestType::ReadAlt => {
368369
let rsp = match req.register {
369370
0x00 => {
@@ -393,9 +394,53 @@ mod app {
393394
});
394395
}
395396
},
396-
None => {}
397+
Some(Message::UartByte(rx_byte)) => {
398+
defmt::info!("UART RX {:?}", rx_byte);
399+
// TODO: Copy byte to software buffer and turn UART RX
400+
// interrupt off if buffer is full
401+
}
402+
None => {
403+
// No messages
404+
}
397405
}
398406

407+
// Look for something in the SPI bytes received buffer:
408+
let mut req = None;
409+
ctx.shared.spi.lock(|spi| {
410+
let mut mark_done = false;
411+
if let Some(data) = spi.get_received() {
412+
use proto::Receivable;
413+
match proto::Request::from_bytes(data) {
414+
Ok(inner_req) => {
415+
mark_done = true;
416+
req = Some(inner_req);
417+
}
418+
Err(proto::Error::BadLength) => {
419+
// Need more data
420+
}
421+
Err(e) => {
422+
defmt::warn!("Bad Req ({:02x})", e as u8);
423+
mark_done = true;
424+
}
425+
}
426+
}
427+
if mark_done {
428+
// Couldn't do this whilst holding the `data` ref.
429+
spi.mark_done();
430+
}
431+
});
432+
433+
// If we got a valid message, queue it so we can look at it next time around
434+
if let Some(req) = req {
435+
if ctx
436+
.shared
437+
.msg_q_in
438+
.lock(|q| q.enqueue(Message::SpiRequest(req)))
439+
.is_err()
440+
{
441+
panic!("Q full!");
442+
}
443+
}
399444
// TODO: Read ADC for 3.3V and 5.0V rails and check good
400445
}
401446
}
@@ -408,8 +453,8 @@ mod app {
408453
#[task(
409454
binds = EXTI4_15,
410455
priority = 4,
411-
shared = [ps2_clk0, ps2_dat0, exti, spi, pin_cs],
412-
local = [kb_decoder, kb_q_in]
456+
shared = [ps2_clk0, msg_q_in, ps2_dat0, exti, spi, pin_cs],
457+
local = [kb_decoder]
413458
)]
414459
fn exti4_15_interrupt(mut ctx: exti4_15_interrupt::Context) {
415460
let pr = ctx.shared.exti.pr.read();
@@ -419,7 +464,14 @@ mod app {
419464
// Do we have a complete word?
420465
if let Some(data) = ctx.local.kb_decoder.add_bit(data_bit) {
421466
// Don't dump in the ISR - we're busy. Add it to this nice lockless queue instead.
422-
ctx.local.kb_q_in.enqueue(data).unwrap();
467+
if ctx
468+
.shared
469+
.msg_q_in
470+
.lock(|q| q.enqueue(Message::Ps2Data0(data)))
471+
.is_err()
472+
{
473+
panic!("queue full");
474+
};
423475
}
424476
// Clear the pending flag for this pin
425477
ctx.shared.exti.pr.write(|w| w.pr15().set_bit());
@@ -442,16 +494,17 @@ mod app {
442494
///
443495
/// It fires whenever there is new data received on USART1. We should flag to the host
444496
/// that data is available.
445-
#[task(binds = USART1, shared = [serial])]
446-
fn usart1_interrupt(ctx: usart1_interrupt::Context) {
497+
#[task(binds = USART1, shared = [serial, msg_q_in])]
498+
fn usart1_interrupt(mut ctx: usart1_interrupt::Context) {
447499
// Reading the register clears the RX-Not-Empty-Interrupt flag.
448500
match ctx.shared.serial.read() {
449501
Ok(b) => {
450-
defmt::info!("<< UART {:x}", b);
451-
}
452-
Err(_) => {
453-
defmt::warn!("<< UART None?");
502+
let _ = ctx
503+
.shared
504+
.msg_q_in
505+
.lock(|q| q.enqueue(Message::UartByte(b)));
454506
}
507+
_ => {}
455508
}
456509
}
457510

0 commit comments

Comments
 (0)