Skip to content

Commit 985b5d1

Browse files
Merge pull request #50 from Neotron-Compute/set_irq_pin
Add interrupt support
2 parents 0ed7303 + 1a82452 commit 985b5d1

File tree

2 files changed

+57
-55
lines changed

2 files changed

+57
-55
lines changed

neotron-bmc-pico/src/main.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use core::convert::TryFrom;
1616
use heapless::spsc::{Consumer, Producer, Queue};
1717
use rtic::app;
1818
use stm32f0xx_hal::{
19-
gpio::gpioa::{PA10, PA11, PA12, PA15, PA2, PA3, PA4, PA9},
19+
gpio::gpioa::{PA10, PA11, PA12, PA15, PA2, PA3, PA4, PA8, PA9},
2020
gpio::gpiob::{PB0, PB1, PB3, PB4, PB5},
2121
gpio::gpiof::{PF0, PF1},
2222
gpio::{Alternate, Floating, Input, Output, PullDown, PullUp, PushPull, AF1},
@@ -165,6 +165,8 @@ mod app {
165165
press_button_reset_short: debouncr::Debouncer<u8, debouncr::Repeat2>,
166166
/// Run-time Clock Control (required for resetting peripheral blocks)
167167
rcc: Option<rcc::Rcc>,
168+
/// IRQ pin
169+
pin_irq: PA8<Output<PushPull>>,
168170
}
169171

170172
#[monotonic(binds = SysTick, default = true)]
@@ -230,6 +232,7 @@ mod app {
230232
pin_sck,
231233
pin_cipo,
232234
pin_copi,
235+
mut pin_irq,
233236
) = cortex_m::interrupt::free(|cs| {
234237
(
235238
// uart_tx,
@@ -273,11 +276,21 @@ mod app {
273276
},
274277
// pin_copi,
275278
gpioa.pa7.into_alternate_af0(cs),
279+
// pin_irq
280+
gpioa.pa8.into_push_pull_output(cs),
276281
)
277282
});
278283

284+
// Put host in reset
279285
pin_sys_reset.set_low().unwrap();
286+
// Turn the PSU off
280287
pin_dc_on.set_low().unwrap();
288+
// IRQ is active low; we have no need for service.
289+
pin_irq.set_high().unwrap();
290+
// Power LED is off
291+
led_power.set_low().unwrap();
292+
// Buzzer is off
293+
_buzzer_pwm.set_low().unwrap();
281294

282295
defmt::info!("Creating UART...");
283296

@@ -293,9 +306,6 @@ mod app {
293306
&mut rcc,
294307
);
295308

296-
led_power.set_low().unwrap();
297-
_buzzer_pwm.set_low().unwrap();
298-
299309
// Set EXTI15 to use PORT A (PA15) - button input
300310
dp.SYSCFG.exticr4.modify(|_r, w| w.exti15().pa15());
301311

@@ -348,6 +358,7 @@ mod app {
348358
press_button_power_long: debouncr::debounce_16(false),
349359
press_button_reset_short: debouncr::debounce_2(false),
350360
rcc: Some(rcc),
361+
pin_irq,
351362
};
352363
let init = init::Monotonics(mono);
353364
(shared_resources, local_resources, init)
@@ -356,7 +367,7 @@ mod app {
356367
/// Our idle task.
357368
///
358369
/// This task is called when there is nothing else to do.
359-
#[idle(shared = [msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset], local = [rcc])]
370+
#[idle(shared = [msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset], local = [rcc, pin_irq])]
360371
fn idle(mut ctx: idle::Context) -> ! {
361372
// TODO: Get this from the VERSION static variable or from PKG_VERSION
362373
let mut register_state = RegisterState {
@@ -366,7 +377,25 @@ mod app {
366377
// Take this out of the `local` object to avoid sharing issues.
367378
let mut rcc = ctx.local.rcc.take().unwrap();
368379
defmt::info!("Idle is running...");
380+
let mut irq_masked = true;
381+
let mut is_high = false;
369382
loop {
383+
if !irq_masked && !register_state.ps2_kb_bytes.is_empty() {
384+
// We need service
385+
ctx.local.pin_irq.set_low().unwrap();
386+
if is_high {
387+
defmt::trace!("irq set");
388+
is_high = false;
389+
}
390+
} else {
391+
// We do not need service
392+
ctx.local.pin_irq.set_high().unwrap();
393+
if !is_high {
394+
defmt::trace!("irq clear");
395+
is_high = true;
396+
}
397+
}
398+
370399
match ctx.shared.msg_q_out.dequeue() {
371400
Some(Message::Ps2Data0(word)) => {
372401
if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) {
@@ -392,11 +421,13 @@ mod app {
392421
.state_dc_power_enabled
393422
.lock(|r| *r = DcPowerState::Off);
394423
// Stop any SPI stuff that's currently going on (the host is about to be powered off)
395-
ctx.shared.spi.lock(|s| s.stop());
424+
ctx.shared.spi.lock(|s| s.reset(&mut rcc));
396425
// Put the host into reset
397426
ctx.shared.pin_sys_reset.lock(|pin| pin.set_low().unwrap());
398427
// Shut off the 5V power
399428
ctx.shared.pin_dc_on.set_low().unwrap();
429+
// Mask the IRQ to avoid back-powering the host
430+
irq_masked = true;
400431
// Start LED blinking again
401432
led_power_blink::spawn().unwrap();
402433
}
@@ -418,6 +449,8 @@ mod app {
418449
// TODO: Take system out of reset when 3.3V and 5.0V are good
419450
// Returns an error if it's already scheduled (but we don't care)
420451
let _ = exit_reset::spawn_after(RESET_DURATION_MS.millis());
452+
// Set 5 - unmask the IRQ
453+
irq_masked = false;
421454
}
422455
}
423456
Some(Message::PowerButtonRelease) => {
@@ -436,8 +469,11 @@ mod app {
436469
// Is the board powered on? Don't do a reset if it's powered off.
437470
if ctx.shared.state_dc_power_enabled.lock(|r| *r) == DcPowerState::On {
438471
defmt::info!("Reset!");
439-
ctx.shared.pin_sys_reset.lock(|pin| pin.set_low().unwrap());
472+
// Step 1 - Stop any SPI stuff that's currently going on (the host is about to be reset)
440473
ctx.shared.spi.lock(|s| s.reset(&mut rcc));
474+
// Step 2 - Hold reset line (active) low
475+
ctx.shared.pin_sys_reset.lock(|pin| pin.set_low().unwrap());
476+
// Step 3 - Take it out of reset in a short while
441477
// Returns an error if it's already scheduled (but we don't care)
442478
let _ = exit_reset::spawn_after(RESET_DURATION_MS.millis());
443479
}
@@ -466,7 +502,7 @@ mod app {
466502
use proto::Receivable;
467503
match proto::Request::from_bytes_with_crc(data, crc) {
468504
Ok(inner_req) => {
469-
defmt::debug!("Got packet");
505+
defmt::trace!("Got packet");
470506
req = Some(inner_req);
471507
}
472508
Err(proto::Error::BadLength) => {
@@ -708,7 +744,7 @@ where
708744
// A duplicate! Resend what we sent last time (so we don't affect FIFOs with a duplicate read).
709745
let length = req.length_or_data as usize;
710746
let rsp = proto::Response::new_ok_with_data(&register_state.scratch[0..length]);
711-
defmt::warn!("Retry");
747+
defmt::debug!("Detected a retry");
712748
rsp_handler(&rsp);
713749
return;
714750
}
@@ -776,11 +812,4 @@ where
776812
// defmt::debug!("Sent {:?}", rsp);
777813
}
778814

779-
// TODO: Pins we haven't used yet
780-
// SPI pins
781-
// spi_clk: gpioa.pa5.into_alternate_af0(cs),
782-
// spi_cipo: gpioa.pa6.into_alternate_af0(cs),
783-
// spi_copi: gpioa.pa7.into_alternate_af0(cs),
784-
// I²C pins
785-
// i2c_scl: gpiob.pb6.into_alternate_af4(cs),
786-
// i2c_sda: gpiob.pb7.into_alternate_af4(cs),
815+
// End of file

neotron-bmc-pico/src/spi.rs

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,6 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
6767

6868
// Enable the SPI device
6969
spi.stop();
70-
spi.dev.cr1.write(|w| {
71-
// Enable the peripheral
72-
w.spe().enabled();
73-
w
74-
});
7570

7671
spi
7772
}
@@ -171,6 +166,7 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
171166
// Tell the SPI engine it has a chip-select
172167
self.dev.cr1.modify(|_r, w| {
173168
w.ssi().slave_selected();
169+
w.spe().enabled();
174170
w
175171
});
176172
}
@@ -179,11 +175,15 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
179175
pub fn stop(&mut self) {
180176
self.dev.cr1.modify(|_r, w| {
181177
w.ssi().slave_not_selected();
178+
w.spe().disabled();
182179
w
183180
});
184181
}
185182

186183
/// Fully reset the SPI peripheral
184+
///
185+
/// This is like calling [`Self::stop()`] but it reboots the IP block to clear any
186+
/// partial words from the RX FIFO.
187187
pub fn reset(&mut self, _rcc: &mut stm32f0xx_hal::rcc::Rcc) {
188188
self.dev.cr1.write(|w| {
189189
// Disable the peripheral
@@ -209,13 +209,8 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
209209
let _ = self.raw_read();
210210
}
211211

212-
// Enable the SPI device and leave it idle
212+
// Leave the SPI device turned off
213213
self.stop();
214-
self.dev.cr1.write(|w| {
215-
// Enable the peripheral
216-
w.spe().enabled();
217-
w
218-
});
219214
}
220215

221216
/// Does the RX FIFO have any data in it?
@@ -293,31 +288,6 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
293288
}
294289
}
295290

296-
/// Load some data into the TX buffer.
297-
///
298-
/// You get an error if you try to load too much.
299-
pub fn set_transmit(&mut self, data: &[u8]) -> Result<(), usize> {
300-
self.tx_ready = 0;
301-
self.tx_idx = 0;
302-
if data.len() > TXC {
303-
// Too much data. This check is important for safety in
304-
// [`Self::tx_isr`].
305-
return Err(TXC);
306-
}
307-
for (inc, space) in data.iter().zip(self.tx_buffer.iter_mut()) {
308-
*space = *inc;
309-
}
310-
// We must never set this to be longer than `TXC` as we do an unchecked
311-
// read from `self.tx_buffer` in [`Self::tx_isr`].
312-
self.tx_ready = data.len();
313-
// Turn on the TX interrupt
314-
self.dev.cr2.write(|w| {
315-
w.txeie().not_masked();
316-
w
317-
});
318-
Ok(())
319-
}
320-
321291
/// Render some message into the TX buffer.
322292
///
323293
/// You get an error if you try to load too much.
@@ -328,11 +298,14 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
328298
self.tx_ready = 0;
329299
self.tx_idx = 0;
330300

331-
match message.render_to_buffer(&mut self.tx_buffer) {
301+
// SPI FIFO seems to corrupt the first byte we load. So load a dummy one
302+
// we don't care about.
303+
self.tx_buffer[0] = 0xFF;
304+
match message.render_to_buffer(&mut self.tx_buffer[1..]) {
332305
Ok(n) => {
333306
// We must never set this to be longer than `TXC` as we do an
334307
// unchecked read from `self.tx_buffer` in [`Self::tx_isr`].
335-
self.tx_ready = n.min(TXC);
308+
self.tx_ready = (n + 1).min(TXC);
336309
// Turn on the TX interrupt
337310
self.dev.cr2.write(|w| {
338311
w.txeie().not_masked();

0 commit comments

Comments
 (0)