forked from M-Labs/thermostat
migrate ad5680 to softspi
This commit is contained in:
parent
ec6aa03dc3
commit
f323c1be63
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -358,6 +358,7 @@ dependencies = [
|
||||
"cortex-m-rt",
|
||||
"hash2hwaddr",
|
||||
"log",
|
||||
"nb",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"panic-abort",
|
||||
|
@ -29,6 +29,7 @@ bit_field = "0.10"
|
||||
byteorder = { version = "1", default-features = false }
|
||||
nom = { version = "5", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
||||
nb = "0.1"
|
||||
|
||||
[patch.crates-io]
|
||||
# TODO: pending https://github.com/stm32-rs/stm32f4xx-hal/pull/125
|
||||
|
@ -32,6 +32,7 @@ mod init_log;
|
||||
use init_log::init_log;
|
||||
mod pins;
|
||||
use pins::Pins;
|
||||
mod softspi;
|
||||
mod ad7172;
|
||||
mod ad5680;
|
||||
mod net;
|
||||
@ -89,6 +90,8 @@ fn main() -> ! {
|
||||
wd.start(WATCHDOG_INTERVAL.ms());
|
||||
wd.feed();
|
||||
|
||||
timer::setup(cp.SYST, clocks);
|
||||
|
||||
let pins = Pins::setup(
|
||||
clocks, dp.TIM1, dp.TIM3,
|
||||
dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOE, dp.GPIOF, dp.GPIOG,
|
||||
@ -98,8 +101,6 @@ fn main() -> ! {
|
||||
let mut channels = Channels::new(pins);
|
||||
channels.calibrate_dac_value(0);
|
||||
|
||||
timer::setup(cp.SYST, clocks);
|
||||
|
||||
#[cfg(not(feature = "generate-hwaddr"))]
|
||||
let hwaddr = EthernetAddress(NET_HWADDR);
|
||||
#[cfg(feature = "generate-hwaddr")]
|
||||
|
44
src/pins.rs
44
src/pins.rs
@ -1,6 +1,6 @@
|
||||
use stm32f4xx_hal::{
|
||||
adc::Adc,
|
||||
hal::{blocking::spi::Transfer, digital::v2::OutputPin},
|
||||
hal::{blocking::spi::Transfer, digital::v2::{InputPin, OutputPin}},
|
||||
gpio::{
|
||||
AF5, Alternate, Analog,
|
||||
gpioa::*,
|
||||
@ -20,6 +20,20 @@ use stm32f4xx_hal::{
|
||||
time::U32Ext,
|
||||
};
|
||||
use crate::channel::{Channel0, Channel1};
|
||||
use crate::softspi::SoftSpi;
|
||||
|
||||
|
||||
pub struct DummyInputPin;
|
||||
|
||||
impl InputPin for DummyInputPin {
|
||||
type Error = (); // `Void`
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(false)
|
||||
}
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait ChannelPins {
|
||||
@ -58,8 +72,8 @@ impl ChannelPins for Channel1 {
|
||||
/// SPI peripheral used for communication with the ADC
|
||||
pub type AdcSpi = Spi<SPI2, (PB10<Alternate<AF5>>, PB14<Alternate<AF5>>, PB15<Alternate<AF5>>)>;
|
||||
pub type AdcNss = PB12<Output<PushPull>>;
|
||||
type Dac0Spi = Spi<SPI4, (PE2<Alternate<AF5>>, NoMiso, PE6<Alternate<AF5>>)>;
|
||||
type Dac1Spi = Spi<SPI5, (PF7<Alternate<AF5>>, NoMiso, PF9<Alternate<AF5>>)>;
|
||||
type Dac0Spi = SoftSpi<PE2<Output<PushPull>>, PE6<Output<PushPull>>, DummyInputPin>;
|
||||
type Dac1Spi = SoftSpi<PF7<Output<PushPull>>, PF9<Output<PushPull>>, DummyInputPin>;
|
||||
|
||||
pub type TecUMeasAdc = Adc<ADC3>;
|
||||
|
||||
@ -196,14 +210,10 @@ impl Pins {
|
||||
clocks: Clocks, spi4: SPI4,
|
||||
sclk: PE2<M1>, sync: PE4<M2>, sdin: PE6<M3>
|
||||
) -> (Dac0Spi, PE4<Output<PushPull>>) {
|
||||
let sclk = sclk.into_alternate_af5();
|
||||
let sdin = sdin.into_alternate_af5();
|
||||
let spi = Spi::spi4(
|
||||
spi4,
|
||||
(sclk, NoMiso, sdin),
|
||||
crate::ad5680::SPI_MODE,
|
||||
crate::ad5680::SPI_CLOCK.into(),
|
||||
clocks
|
||||
let sclk = sclk.into_push_pull_output();
|
||||
let sdin = sdin.into_push_pull_output();
|
||||
let spi = SoftSpi::new(
|
||||
sclk, sdin, DummyInputPin,
|
||||
);
|
||||
let sync = sync.into_push_pull_output();
|
||||
|
||||
@ -214,14 +224,10 @@ impl Pins {
|
||||
clocks: Clocks, spi5: SPI5,
|
||||
sclk: PF7<M1>, sync: PF6<M2>, sdin: PF9<M3>
|
||||
) -> (Dac1Spi, PF6<Output<PushPull>>) {
|
||||
let sclk = sclk.into_alternate_af5();
|
||||
let sdin = sdin.into_alternate_af5();
|
||||
let spi = Spi::spi5(
|
||||
spi5,
|
||||
(sclk, NoMiso, sdin),
|
||||
crate::ad5680::SPI_MODE,
|
||||
crate::ad5680::SPI_CLOCK.into(),
|
||||
clocks
|
||||
let sclk = sclk.into_push_pull_output();
|
||||
let sdin = sdin.into_push_pull_output();
|
||||
let spi = SoftSpi::new(
|
||||
sclk, sdin, DummyInputPin,
|
||||
);
|
||||
let sync = sync.into_push_pull_output();
|
||||
|
||||
|
147
src/softspi.rs
Normal file
147
src/softspi.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use stm32f4xx_hal::hal::{
|
||||
spi::FullDuplex,
|
||||
digital::v2::{InputPin, OutputPin},
|
||||
blocking::spi::{Transfer, transfer},
|
||||
};
|
||||
use nb::{block, Error, Error::WouldBlock};
|
||||
use crate::timer::now;
|
||||
|
||||
/// Bit-banged Mode3 SPI
|
||||
pub struct SoftSpi<SCK, MOSI, MISO> {
|
||||
sck: SCK,
|
||||
mosi: MOSI,
|
||||
miso: MISO,
|
||||
state: State,
|
||||
input: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum State {
|
||||
Idle,
|
||||
Transfer {
|
||||
clock_phase: bool,
|
||||
mask: u8,
|
||||
output: u8,
|
||||
input: u8,
|
||||
},
|
||||
}
|
||||
|
||||
impl<SCK: OutputPin, MOSI: OutputPin, MISO: InputPin> SoftSpi<SCK, MOSI, MISO> {
|
||||
pub fn new(mut sck: SCK, mut mosi: MOSI, miso: MISO) -> Self {
|
||||
let _ = sck.set_high();
|
||||
let _ = mosi.set_low();
|
||||
SoftSpi {
|
||||
sck, mosi, miso,
|
||||
state: State::Idle,
|
||||
input: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this at twice the data rate
|
||||
pub fn tick(&mut self) {
|
||||
match self.state {
|
||||
State::Idle => {}
|
||||
State::Transfer { clock_phase: false,
|
||||
mask, output, input } => {
|
||||
if output & mask != 0 {
|
||||
let _ = self.mosi.set_high();
|
||||
} else {
|
||||
let _ = self.mosi.set_low();
|
||||
}
|
||||
let _ = self.sck.set_low();
|
||||
|
||||
self.state = State::Transfer {
|
||||
clock_phase: true,
|
||||
mask, output, input,
|
||||
};
|
||||
}
|
||||
State::Transfer { clock_phase: true,
|
||||
mask, output, mut input } => {
|
||||
if self.miso.is_high().unwrap_or(false) {
|
||||
input |= mask;
|
||||
}
|
||||
let _ = self.sck.set_high();
|
||||
|
||||
if mask != 1 {
|
||||
self.state = State::Transfer {
|
||||
clock_phase: false,
|
||||
mask: mask >> 1,
|
||||
output, input,
|
||||
};
|
||||
} else {
|
||||
self.input = Some(input);
|
||||
self.state = State::Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
while self.state != State::Idle {
|
||||
self.tick();
|
||||
spi_delay();
|
||||
}
|
||||
}
|
||||
|
||||
fn retry<R, E, F>(&mut self, f: &F) -> Result<R, E>
|
||||
where
|
||||
F: Fn(&'_ mut SoftSpi<SCK, MOSI, MISO>) -> Result<R, nb::Error<E>>
|
||||
{
|
||||
loop {
|
||||
match f(self) {
|
||||
Ok(r) => return Ok(r),
|
||||
Err(nb::Error::Other(e)) => return Err(e),
|
||||
Err(WouldBlock) => self.run(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SCK: OutputPin, MOSI: OutputPin, MISO: InputPin> FullDuplex<u8> for SoftSpi<SCK, MOSI, MISO> {
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
match self.input.take() {
|
||||
Some(input) =>
|
||||
Ok(input),
|
||||
None if self.state == State::Idle =>
|
||||
Err(nb::Error::Other(())),
|
||||
None =>
|
||||
Err(WouldBlock),
|
||||
}
|
||||
}
|
||||
|
||||
fn send(&mut self, output: u8) -> Result<(), nb::Error<Self::Error>> {
|
||||
match self.state {
|
||||
State::Idle => {
|
||||
self.state = State::Transfer {
|
||||
clock_phase: false,
|
||||
mask: 0x80,
|
||||
output,
|
||||
input: 0,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SCK: OutputPin, MOSI: OutputPin, MISO: InputPin> Transfer<u8> for SoftSpi<SCK, MOSI, MISO> {
|
||||
// TODO: proper type
|
||||
type Error = ();
|
||||
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
|
||||
for b in words.iter_mut() {
|
||||
self.retry(&|spi| spi.send(*b))?;
|
||||
*b = self.retry(&|spi| spi.read())?;
|
||||
}
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
|
||||
fn spi_delay() {
|
||||
const DELAY: u32 = 1;
|
||||
|
||||
let start = now();
|
||||
while now() - start < DELAY {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user