-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restructure and implement embedded-nal UDP traits #26
Changes from all commits
03e30ef
a43f86d
16e813e
aa0c69b
d47de54
3177bad
ce36644
7dd4c04
fd9e861
d2fb6b9
e74f7f4
b30e4d0
e8c8e3c
bd78b82
715cdea
b82bb92
65a9552
36df284
95ca1be
d0f5792
423d2f6
332ab92
63890a5
b9f9166
b6a52cb
adc7005
4ff4bc9
3dbb2d4
074e01e
41cd42e
8aa5656
b1e83e3
f546ff2
3cad9ca
1d533b2
79b1f52
6caeeae
79dc9a4
cc4db22
1cac758
dcfa655
839367b
2b59081
2f26a50
90604fc
259da58
d3ca4a3
da2bcd4
9a05719
d844a11
d6574b8
5509732
ca8268a
a3e0911
daa1c00
b49f41c
dfa252f
52f0d06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#![allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)] | ||
|
||
use core::fmt; | ||
use embedded_hal::blocking::spi::{Transfer, Write}; | ||
use embedded_hal::digital::v2::OutputPin; | ||
|
||
use crate::bus::Bus; | ||
|
||
const WRITE_MODE_MASK: u8 = 0b00000_1_00; | ||
|
||
// TODO This name is not ideal, should be renamed to VDM | ||
pub struct FourWire<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> { | ||
cs: ChipSelect, | ||
spi: Spi, | ||
} | ||
|
||
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> FourWire<Spi, ChipSelect> { | ||
pub fn new(spi: Spi, cs: ChipSelect) -> Self { | ||
Self { spi, cs } | ||
} | ||
|
||
pub fn release(self) -> (Spi, ChipSelect) { | ||
(self.spi, self.cs) | ||
} | ||
} | ||
|
||
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> Bus for FourWire<Spi, ChipSelect> { | ||
type Error = | ||
FourWireError<<Spi as Transfer<u8>>::Error, <Spi as Write<u8>>::Error, ChipSelect::Error>; | ||
fn read_frame(&mut self, block: u8, address: u16, data: &mut [u8]) -> Result<(), Self::Error> { | ||
let address_phase = address.to_be_bytes(); | ||
let control_phase = block << 3; | ||
let data_phase = data; | ||
self.cs.set_low().map_err(FourWireError::ChipSelectError)?; | ||
self.spi | ||
.write(&address_phase) | ||
.and_then(|_| self.spi.write(&[control_phase])) | ||
.map_err(FourWireError::WriteError)?; | ||
self.spi | ||
.transfer(data_phase) | ||
.map_err(FourWireError::TransferError)?; | ||
self.cs.set_high().map_err(FourWireError::ChipSelectError)?; | ||
|
||
Ok(()) | ||
} | ||
fn write_frame(&mut self, block: u8, address: u16, data: &[u8]) -> Result<(), Self::Error> { | ||
let address_phase = address.to_be_bytes(); | ||
let control_phase = block << 3 | WRITE_MODE_MASK; | ||
let data_phase = data; | ||
self.cs.set_low().map_err(FourWireError::ChipSelectError)?; | ||
self.spi | ||
.write(&address_phase) | ||
.and_then(|_| self.spi.write(&[control_phase])) | ||
.and_then(|_| self.spi.write(data_phase)) | ||
.map_err(FourWireError::WriteError)?; | ||
self.cs.set_high().map_err(FourWireError::ChipSelectError)?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
// Must use map_err, ambiguity prevents From from being implemented | ||
#[repr(u8)] | ||
pub enum FourWireError<TransferError, WriteError, ChipSelectError> { | ||
TransferError(TransferError), | ||
WriteError(WriteError), | ||
ChipSelectError(ChipSelectError), | ||
} | ||
|
||
impl<TransferError, WriteError, ChipSelectError> fmt::Debug | ||
for FourWireError<TransferError, WriteError, ChipSelectError> | ||
{ | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!( | ||
f, | ||
"FourWireError::{}", | ||
match self { | ||
Self::TransferError(_) => "TransferError", | ||
Self::WriteError(_) => "WriteError", | ||
Self::ChipSelectError(_) => "ChipSelectError", | ||
} | ||
) | ||
} | ||
} | ||
|
||
// TODO Improved error rendering could be done with specialization. | ||
// https://github.com/rust-lang/rust/issues/31844 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use core::fmt::Debug; | ||
|
||
mod four_wire; | ||
mod three_wire; | ||
|
||
pub use self::four_wire::FourWire; | ||
pub use self::three_wire::ThreeWire; | ||
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How to access FourWire/ThreeWire Error when not public? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you mean, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wasn't public before. Otherwise one could not work with the FourWireError type? |
||
|
||
pub trait Bus { | ||
type Error: Debug; | ||
|
||
fn read_frame(&mut self, block: u8, address: u16, data: &mut [u8]) -> Result<(), Self::Error>; | ||
|
||
fn write_frame(&mut self, block: u8, address: u16, data: &[u8]) -> Result<(), Self::Error>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#![allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)] | ||
|
||
use core::fmt; | ||
use embedded_hal::blocking::spi::{Transfer, Write}; | ||
|
||
use crate::bus::Bus; | ||
|
||
const WRITE_MODE_MASK: u8 = 0b00000_1_0; | ||
|
||
const FIXED_DATA_LENGTH_MODE_1: u8 = 0b000000_01; | ||
const FIXED_DATA_LENGTH_MODE_2: u8 = 0b000000_10; | ||
const FIXED_DATA_LENGTH_MODE_4: u8 = 0b000000_11; | ||
|
||
// TODO This name is not ideal, should be renamed to FDM | ||
pub struct ThreeWire<Spi: Transfer<u8> + Write<u8>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What stands FDM for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed length Data Mode (see screenshot of datasheet above). |
||
spi: Spi, | ||
} | ||
|
||
impl<Spi: Transfer<u8> + Write<u8>> ThreeWire<Spi> { | ||
pub fn new(spi: Spi) -> Self { | ||
Self { spi } | ||
} | ||
|
||
pub fn release(self) -> Spi { | ||
self.spi | ||
} | ||
} | ||
|
||
impl<Spi: Transfer<u8> + Write<u8>> Bus for ThreeWire<Spi> { | ||
type Error = ThreeWireError<<Spi as Transfer<u8>>::Error, <Spi as Write<u8>>::Error>; | ||
|
||
/// Transfers a frame with an arbitrary data length in FDM | ||
/// | ||
/// This is done by passing chunks of fixed length 4, 2, or 1. For example if a frame looks like this: | ||
/// | ||
/// (address 23) 0xF0 0xAB 0x83 0xB2 0x44 0x2C 0xAA | ||
/// | ||
/// This will be sent as separate frames in the chunks | ||
/// | ||
/// (address 23) 0xF0 0xAB 0x83 0xB2 | ||
/// (address 27) 44 2C | ||
/// (address 29) AA | ||
Comment on lines
+32
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the advantage of this behavior? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two modes are for when you either have or do not have a CS pin. You can connect with only three wires (MISO, MOSI, CLK), but you have to use FDM. |
||
fn read_frame( | ||
&mut self, | ||
block: u8, | ||
mut address: u16, | ||
data: &mut [u8], | ||
) -> Result<(), Self::Error> { | ||
let mut control_phase = block << 3; | ||
|
||
let mut data_phase = &mut data[..]; | ||
let mut last_length_written: u16; | ||
while !data_phase.is_empty() { | ||
if data_phase.len() >= 4 { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_4; | ||
last_length_written = 4; | ||
} else if data_phase.len() >= 2 { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_2; | ||
last_length_written = 2; | ||
} else { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_1; | ||
last_length_written = 1; | ||
} | ||
|
||
let address_phase = address.to_be_bytes(); | ||
self.spi | ||
.write(&address_phase) | ||
.and_then(|_| self.spi.write(&[control_phase])) | ||
.map_err(ThreeWireError::WriteError)?; | ||
self.spi | ||
.transfer(&mut data_phase[..last_length_written as usize]) | ||
.map_err(ThreeWireError::TransferError)?; | ||
|
||
address += last_length_written; | ||
data_phase = &mut data_phase[last_length_written as usize..]; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn write_frame(&mut self, block: u8, mut address: u16, data: &[u8]) -> Result<(), Self::Error> { | ||
let mut control_phase = block << 3 | WRITE_MODE_MASK; | ||
|
||
let mut data_phase = &data[..]; | ||
let mut last_length_written: u16; | ||
while !data_phase.is_empty() { | ||
if data_phase.len() >= 4 { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_4; | ||
last_length_written = 4; | ||
} else if data_phase.len() >= 2 { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_2; | ||
last_length_written = 2; | ||
} else { | ||
control_phase |= FIXED_DATA_LENGTH_MODE_1; | ||
last_length_written = 1; | ||
} | ||
|
||
let address_phase = address.to_be_bytes(); | ||
self.spi | ||
.write(&address_phase) | ||
.and_then(|_| self.spi.write(&[control_phase])) | ||
.and_then(|_| self.spi.write(&data_phase[..last_length_written as usize])) | ||
.map_err(ThreeWireError::WriteError)?; | ||
|
||
address += last_length_written; | ||
data_phase = &data_phase[last_length_written as usize..]; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
// Must use map_err, ambiguity prevents From from being implemented | ||
pub enum ThreeWireError<TransferError, WriteError> { | ||
TransferError(TransferError), | ||
WriteError(WriteError), | ||
} | ||
|
||
impl<TransferError, WriteError> fmt::Debug for ThreeWireError<TransferError, WriteError> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!( | ||
f, | ||
"ThreeWireError::{}", | ||
match self { | ||
Self::TransferError(_) => "TransferError", | ||
Self::WriteError(_) => "WriteError", | ||
} | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What stands VDM for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable length Data Mode, as defined in the data sheet.