Skip to content

Commit b9de248

Browse files
authored
Merge pull request #44 from Neotron-Compute/fix-reset
Force-reset the SPI block when you reset the host.
2 parents 0efe4a8 + 1d426f0 commit b9de248

File tree

2 files changed

+82
-34
lines changed

2 files changed

+82
-34
lines changed

neotron-bmc-pico/src/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use stm32f0xx_hal::{
2222
gpio::{Alternate, Floating, Input, Output, PullDown, PullUp, PushPull, AF1},
2323
pac,
2424
prelude::*,
25-
serial,
25+
rcc, serial,
2626
};
2727

2828
use neotron_bmc_commands::Command;
@@ -163,6 +163,8 @@ mod app {
163163
press_button_power_long: debouncr::Debouncer<u16, debouncr::Repeat16>,
164164
/// Tracks reset button state for short presses. 75ms x 2 = 150ms is a long press
165165
press_button_reset_short: debouncr::Debouncer<u8, debouncr::Repeat2>,
166+
/// Run-time Clock Control (required for resetting peripheral blocks)
167+
rcc: Option<rcc::Rcc>,
166168
}
167169

168170
#[monotonic(binds = SysTick, default = true)]
@@ -342,6 +344,7 @@ mod app {
342344
press_button_power_short: debouncr::debounce_2(false),
343345
press_button_power_long: debouncr::debounce_16(false),
344346
press_button_reset_short: debouncr::debounce_2(false),
347+
rcc: Some(rcc),
345348
};
346349
let init = init::Monotonics(mono);
347350
(shared_resources, local_resources, init)
@@ -350,14 +353,15 @@ mod app {
350353
/// Our idle task.
351354
///
352355
/// This task is called when there is nothing else to do.
353-
#[idle(shared = [msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset])]
356+
#[idle(shared = [msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset], local = [rcc])]
354357
fn idle(mut ctx: idle::Context) -> ! {
355358
// TODO: Get this from the VERSION static variable or from PKG_VERSION
356359
let mut register_state = RegisterState {
357360
firmware_version: *b"Neotron BMC v0.4.1-alpha\x00\x00\x00\x00\x00\x00\x00\x00",
358361
..Default::default()
359362
};
360-
363+
// Take this out of the `local` object to avoid sharing issues.
364+
let mut rcc = ctx.local.rcc.take().unwrap();
361365
defmt::info!("Idle is running...");
362366
loop {
363367
match ctx.shared.msg_q_out.dequeue() {
@@ -430,6 +434,7 @@ mod app {
430434
if ctx.shared.state_dc_power_enabled.lock(|r| *r) == DcPowerState::On {
431435
defmt::info!("Reset!");
432436
ctx.shared.pin_sys_reset.lock(|pin| pin.set_low().unwrap());
437+
ctx.shared.spi.lock(|s| s.reset(&mut rcc));
433438
// Returns an error if it's already scheduled (but we don't care)
434439
let _ = exit_reset::spawn_after(RESET_DURATION_MS.millis());
435440
}

neotron-bmc-pico/src/spi.rs

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use stm32f0xx_hal::{pac, prelude::*, rcc::Rcc};
77

88
pub struct SpiPeripheral<const RXC: usize, const TXC: usize> {
9+
/// Our PAC object for register access
910
dev: pac::SPI1,
1011
/// A space for bytes received from the host
1112
rx_buffer: [u8; RXC],
@@ -23,6 +24,9 @@ pub struct SpiPeripheral<const RXC: usize, const TXC: usize> {
2324
}
2425

2526
impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
27+
const MODE: embedded_hal::spi::Mode = embedded_hal::spi::MODE_0;
28+
29+
/// Construct a new driver
2630
pub fn new<SCKPIN, MISOPIN, MOSIPIN>(
2731
dev: pac::SPI1,
2832
pins: (SCKPIN, MISOPIN, MOSIPIN),
@@ -35,24 +39,53 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
3539
{
3640
defmt::info!("pclk = {}", rcc.clocks.pclk().0,);
3741

38-
let mode = embedded_hal::spi::MODE_0;
39-
4042
// Set SPI up in Controller mode. This will cause the HAL to enable the clocks and power to the IP block.
4143
// It also checks the pins are OK.
42-
let spi_controller = stm32f0xx_hal::spi::Spi::spi1(dev, pins, mode, 8_000_000u32.hz(), rcc);
44+
let spi_controller =
45+
stm32f0xx_hal::spi::Spi::spi1(dev, pins, Self::MODE, 8_000_000u32.hz(), rcc);
4346
// Now disassemble the driver so we can set it into Controller mode instead
4447
let (dev, _pins) = spi_controller.release();
4548

49+
let mut spi = SpiPeripheral {
50+
dev,
51+
rx_buffer: [0u8; RXC],
52+
rx_idx: 0,
53+
tx_buffer: [0u8; TXC],
54+
tx_idx: 0,
55+
tx_ready: 0,
56+
is_done: false,
57+
};
58+
59+
spi.config(Self::MODE);
60+
61+
// Empty the receive register
62+
while spi.has_rx_data() {
63+
let _ = spi.raw_read();
64+
}
65+
66+
// Enable the SPI device
67+
spi.stop();
68+
spi.dev.cr1.write(|w| {
69+
// Enable the peripheral
70+
w.spe().enabled();
71+
w
72+
});
73+
74+
spi
75+
}
76+
77+
/// Set up the registers
78+
fn config(&mut self, mode: embedded_hal::spi::Mode) {
4679
// We are following DM00043574, Section 30.5.1 Configuration of SPI
4780

4881
// 1. Disable SPI
49-
dev.cr1.modify(|_r, w| {
82+
self.dev.cr1.modify(|_r, w| {
5083
w.spe().disabled();
5184
w
5285
});
5386

5487
// 2. Write to the SPI_CR1 register. Apologies for the outdated terminology.
55-
dev.cr1.write(|w| {
88+
self.dev.cr1.write(|w| {
5689
// 2a. Configure the serial clock baud rate (ignored in peripheral mode)
5790
w.br().div2();
5891
// 2b. Configure the CPHA and CPOL bits.
@@ -83,7 +116,7 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
83116
});
84117

85118
// 3. Write to SPI_CR2 register
86-
dev.cr2.write(|w| {
119+
self.dev.cr2.write(|w| {
87120
// 3a. Configure the DS[3:0] bits to select the data length for the transfer (0b111 = 8-bit words).
88121
unsafe { w.ds().bits(0b111) };
89122
// 3b. Disable hard-output on the CS pin (ignored in Master mode)
@@ -109,31 +142,6 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
109142
// 4. SPI_CRCPR - not required
110143

111144
// 5. DMA registers - not required
112-
113-
let mut spi = SpiPeripheral {
114-
dev,
115-
rx_buffer: [0u8; RXC],
116-
rx_idx: 0,
117-
tx_buffer: [0u8; TXC],
118-
tx_idx: 0,
119-
tx_ready: 0,
120-
is_done: false,
121-
};
122-
123-
// Empty the receive register
124-
while spi.has_rx_data() {
125-
let _ = spi.raw_read();
126-
}
127-
128-
// Enable the SPI device
129-
spi.stop();
130-
spi.dev.cr1.write(|w| {
131-
// Enable the peripheral
132-
w.spe().enabled();
133-
w
134-
});
135-
136-
spi
137145
}
138146

139147
/// Enable the SPI peripheral (i.e. when CS goes low)
@@ -160,6 +168,41 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
160168
});
161169
}
162170

171+
/// Fully reset the SPI peripheral
172+
pub fn reset(&mut self, _rcc: &mut stm32f0xx_hal::rcc::Rcc) {
173+
self.dev.cr1.write(|w| {
174+
// Disable the peripheral
175+
w.spe().disabled();
176+
w
177+
});
178+
179+
// Reset the IP manually. This is OK as we have exclusive access to the
180+
// RCC peripheral. But sadly the RCC peripheral doesn't let us reset
181+
// anything (it assumes it can handle it all internally).
182+
let reset_reg = 0x4002_100C as *mut u32;
183+
let spi1_bit = 1 << 12;
184+
unsafe {
185+
*reset_reg |= spi1_bit;
186+
*reset_reg &= !(spi1_bit);
187+
}
188+
189+
// Reconfigure
190+
self.config(Self::MODE);
191+
192+
// Empty the receive register
193+
while self.has_rx_data() {
194+
let _ = self.raw_read();
195+
}
196+
197+
// Enable the SPI device and leave it idle
198+
self.stop();
199+
self.dev.cr1.write(|w| {
200+
// Enable the peripheral
201+
w.spe().enabled();
202+
w
203+
});
204+
}
205+
163206
/// Does the RX FIFO have any data in it?
164207
fn has_rx_data(&self) -> bool {
165208
self.dev.sr.read().rxne().is_not_empty()

0 commit comments

Comments
 (0)