Skip to content

Commit 381ad79

Browse files
Speak new SPI protocol.
1 parent 93addad commit 381ad79

File tree

5 files changed

+89
-50
lines changed

5 files changed

+89
-50
lines changed

neotron-bmc-pico/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +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" }
1617
systick-monotonic = "1.0"
1718
embedded-hal = "*"
1819

neotron-bmc-pico/src/main.rs

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use stm32f0xx_hal::{
2424
};
2525

2626
use neotron_bmc_pico as _;
27+
use neotron_bmc_protocol as proto;
2728

2829
/// Version string auto-generated by git.
2930
static VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
@@ -52,7 +53,7 @@ pub enum DcPowerState {
5253
/// This is our system state, as accessible via SPI reads and writes.
5354
#[derive(Debug)]
5455
pub struct RegisterState {
55-
firmware_version: &'static str,
56+
firmware_version: [u8; 32],
5657
}
5758

5859
#[app(device = crate::pac, peripherals = true, dispatchers = [USB, USART3_4_5_6, TIM14, TIM15, TIM16, TIM17, PVD])]
@@ -303,7 +304,8 @@ mod app {
303304
_ps2_dat1,
304305
exti: dp.EXTI,
305306
register_state: RegisterState {
306-
firmware_version: concat!("Neotron BMC ", env!("CARGO_PKG_VERSION")),
307+
firmware_version:
308+
*b"Neotron BMC v0.3.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
307309
},
308310
kb_q_out,
309311
spi,
@@ -323,7 +325,7 @@ mod app {
323325
/// Our idle task.
324326
///
325327
/// This task is called when there is nothing else to do.
326-
#[idle(shared = [kb_q_out, spi])]
328+
#[idle(shared = [kb_q_out, spi, register_state])]
327329
fn idle(mut ctx: idle::Context) -> ! {
328330
defmt::info!("Idle is running...");
329331
loop {
@@ -335,27 +337,63 @@ mod app {
335337
}
336338
}
337339

338-
let mut rx_buffer = [0u8; 3];
339-
let mut has_packet = false;
340+
let mut req = None;
340341
ctx.shared.spi.lock(|spi| {
341342
let mut mark_done = false;
342343
if let Some(data) = spi.get_received() {
343-
if data.len() == 3 {
344-
rx_buffer.copy_from_slice(data);
345-
mark_done = true;
346-
has_packet = true;
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+
}
347357
}
348358
}
349359
if mark_done {
350-
spi.set_transmit(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])
351-
.unwrap();
360+
// Couldn't do this whilst holding the `data` ref.
352361
spi.mark_done();
353362
}
354363
});
355-
if has_packet {
356-
if rx_buffer != [0xAA, 0x10, 0x01] {
357-
defmt::info!("Bad RX {:?}", rx_buffer);
358-
}
364+
365+
match req {
366+
Some(req) => match req.request_type {
367+
proto::RequestType::Read | proto::RequestType::ReadAlt => {
368+
let rsp = match req.register {
369+
0x00 => {
370+
let length = req.length_or_data as usize;
371+
if length > ctx.shared.register_state.firmware_version.len() {
372+
proto::Response::new_without_data(
373+
proto::ResponseResult::BadLength,
374+
)
375+
} else {
376+
let bytes = &ctx.shared.register_state.firmware_version;
377+
proto::Response::new_ok_with_data(&bytes[0..length])
378+
}
379+
}
380+
_ => proto::Response::new_without_data(
381+
proto::ResponseResult::BadRegister,
382+
),
383+
};
384+
ctx.shared.spi.lock(|spi| {
385+
spi.set_transmit_sendable(&rsp).unwrap();
386+
});
387+
}
388+
_ => {
389+
let rsp =
390+
proto::Response::new_without_data(proto::ResponseResult::BadLength);
391+
ctx.shared.spi.lock(|spi| {
392+
spi.set_transmit_sendable(&rsp).unwrap();
393+
});
394+
}
395+
},
396+
None => {}
359397
}
360398

361399
// TODO: Read ADC for 3.3V and 5.0V rails and check good
@@ -421,7 +459,7 @@ mod app {
421459
///
422460
/// It fires whenever there is new data received on SPI1. We should flag to the host
423461
/// that data is available.
424-
#[task(binds = SPI1, shared = [spi, register_state])]
462+
#[task(binds = SPI1, shared = [spi])]
425463
fn spi1_interrupt(mut ctx: spi1_interrupt::Context) {
426464
ctx.shared.spi.lock(|spi| {
427465
spi.handle_isr();

neotron-bmc-pico/src/spi.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
//! Unlike the HAL, this implement 'SPI Peripheral Mode', i.e. for when the
44
//! clock signal is an input and not an output.
55
6-
use core::convert::TryInto;
7-
86
use stm32f0xx_hal::{pac, prelude::*, rcc::Rcc};
97

108
pub struct SpiPeripheral<const RXC: usize, const TXC: usize> {
@@ -143,35 +141,17 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
143141
self.raw_write(0xFF);
144142
// Get an IRQ when there's RX data available
145143
self.enable_rxne_irq();
146-
// self.disable_txe_irq();
147144
}
148145

149146
/// Disable the SPI peripheral (i.e. when CS is high)
150147
pub fn disable(&mut self) {
151-
self.disable_txe_irq();
152148
self.disable_rxne_irq();
153149
self.dev.cr1.modify(|_r, w| {
154150
w.spe().disabled();
155151
w
156152
});
157153
}
158154

159-
/// Enable TX Empty interrupt
160-
fn enable_txe_irq(&mut self) {
161-
self.dev.cr2.modify(|_r, w| {
162-
w.txeie().set_bit();
163-
w
164-
});
165-
}
166-
167-
/// Disable TX Empty interrupt
168-
fn disable_txe_irq(&mut self) {
169-
self.dev.cr2.modify(|_r, w| {
170-
w.txeie().clear_bit();
171-
w
172-
});
173-
}
174-
175155
/// Enable RX Not Empty interrupt
176156
fn enable_rxne_irq(&mut self) {
177157
self.dev.cr2.modify(|_r, w| {
@@ -269,8 +249,25 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
269249
*space = *inc;
270250
}
271251
self.tx_ready = data.len();
272-
// Start the IRQ driven data transmission
273-
// self.enable_txe_irq();
274252
Ok(())
275253
}
254+
255+
/// Render some message into the TX buffer.
256+
///
257+
/// You get an error if you try to load too much.
258+
pub fn set_transmit_sendable(
259+
&mut self,
260+
message: &dyn neotron_bmc_protocol::Sendable,
261+
) -> Result<(), ()> {
262+
self.tx_ready = 0;
263+
self.tx_idx = 0;
264+
265+
match message.render_to_buffer(&mut self.tx_buffer) {
266+
Ok(n) => {
267+
self.tx_ready = n;
268+
Ok(())
269+
}
270+
Err(_) => Err(()),
271+
}
272+
}
276273
}

neotron-bmc-protocol/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ license = "BlueOak-1.0.0"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
defmt = "0.3"

neotron-bmc-protocol/src/lib.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// Modules and Imports
66
// ============================================================================
77

8+
use defmt::Format;
9+
810
mod crc;
911

1012
// ============================================================================
@@ -32,7 +34,7 @@ pub trait Receivable<'a>: Sized {
3234
// ============================================================================
3335

3436
/// The ways this API can fail
35-
#[derive(Debug, Copy, Clone)]
37+
#[derive(Debug, Copy, Clone, Format)]
3638
pub enum Error {
3739
BadCrc,
3840
BadLength,
@@ -43,7 +45,7 @@ pub enum Error {
4345

4446
/// The kinds of [`Request`] the *Host* can make to the NBMC
4547
#[repr(u8)]
46-
#[derive(Debug, Copy, Clone)]
48+
#[derive(Debug, Copy, Clone, Format)]
4749
pub enum RequestType {
4850
Read = 0xC0,
4951
ReadAlt = 0xC1,
@@ -55,7 +57,7 @@ pub enum RequestType {
5557

5658
/// The NBMC returns this code to indicate whether the previous [`Request`] was
5759
/// succesful or not.
58-
#[derive(Debug, Copy, Clone)]
60+
#[derive(Debug, Copy, Clone, Format)]
5961
pub enum ResponseResult {
6062
/// The [`Request`] was correctly understood and actioned.
6163
Ok = 0xA0,
@@ -83,7 +85,7 @@ pub enum ResponseResult {
8385
// ============================================================================
8486

8587
/// A *Request* made by the *Host* to the *NBMC*
86-
#[derive(Debug, Clone)]
88+
#[derive(Debug, Clone, Format)]
8789
pub struct Request {
8890
pub request_type: RequestType,
8991
pub register: u8,
@@ -92,7 +94,7 @@ pub struct Request {
9294
}
9395

9496
/// A *Response* sent by the *NBMC* in reply to a [`Request`] from a *Host*
95-
#[derive(Debug, Clone)]
97+
#[derive(Debug, Clone, Format)]
9698
pub struct Response<'a> {
9799
pub result: ResponseResult,
98100
pub data: &'a [u8],
@@ -101,7 +103,7 @@ pub struct Response<'a> {
101103

102104
/// Describes the [semantic version](https://semver.org) of this implementation
103105
/// of the NBMC interface.
104-
#[derive(Debug, Copy, Clone)]
106+
#[derive(Debug, Copy, Clone, Format)]
105107
pub struct ProtocolVersion {
106108
major: u8,
107109
minor: u8,
@@ -232,7 +234,7 @@ impl<'a> Receivable<'a> for Request {
232234
/// let req = Request::from_bytes(&bytes).unwrap();
233235
/// ```
234236
fn from_bytes(data: &'a [u8]) -> Result<Request, Error> {
235-
if data.len() != 4 {
237+
if data.len() < 4 {
236238
return Err(Error::BadLength);
237239
}
238240
let calc_crc = calculate_crc(&data[0..=3]);
@@ -267,7 +269,7 @@ impl TryFrom<u8> for ResponseResult {
267269

268270
impl<'a> Response<'a> {
269271
/// Make a new OK response, with some optional data
270-
pub fn ok_with_data(data: &'a [u8]) -> Response<'a> {
272+
pub fn new_ok_with_data(data: &'a [u8]) -> Response<'a> {
271273
Response {
272274
result: ResponseResult::Ok,
273275
data,
@@ -281,7 +283,7 @@ impl<'a> Response<'a> {
281283
}
282284

283285
/// Make a new error response
284-
pub fn new(result: ResponseResult) -> Response<'a> {
286+
pub fn new_without_data(result: ResponseResult) -> Response<'a> {
285287
Response {
286288
result,
287289
data: &[],
@@ -299,15 +301,15 @@ impl<'a> Sendable for Response<'a> {
299301
/// # use neotron_bmc_protocol::{Response, ResponseResult, Sendable};
300302
/// let mut buffer = [0u8; 5];
301303
///
302-
/// let req = Response::ok_with_data(&[]);
304+
/// let req = Response::new_ok_with_data(&[]);
303305
/// assert_eq!(req.render_to_buffer(&mut buffer).unwrap(), 2);
304306
/// assert_eq!(&buffer[0..=1], [0xA0, 0x69]);
305307
///
306-
/// let req = Response::ok_with_data(&[0x00, 0x01]);
308+
/// let req = Response::new_ok_with_data(&[0x00, 0x01]);
307309
/// assert_eq!(req.render_to_buffer(&mut buffer).unwrap(), 4);
308310
/// assert_eq!(&buffer[0..=3], [0xA0, 0x00, 0x01, 0x4F]);
309311
///
310-
/// let req = Response::new(ResponseResult::BadRequestType);
312+
/// let req = Response::new_without_data(ResponseResult::BadRequestType);
311313
/// assert_eq!(req.render_to_buffer(&mut buffer).unwrap(), 2);
312314
/// assert_eq!(&buffer[0..=1], [0xA2, 0x67]);
313315
/// ```

0 commit comments

Comments
 (0)