forked from M-Labs/thermostat
migrate ad5680 to softspi
This commit is contained in:
parent
ec6aa03dc3
commit
f323c1be63
|
@ -358,6 +358,7 @@ dependencies = [
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"hash2hwaddr",
|
"hash2hwaddr",
|
||||||
"log",
|
"log",
|
||||||
|
"nb",
|
||||||
"nom",
|
"nom",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"panic-abort",
|
"panic-abort",
|
||||||
|
|
|
@ -29,6 +29,7 @@ bit_field = "0.10"
|
||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false }
|
||||||
nom = { version = "5", default-features = false }
|
nom = { version = "5", default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
||||||
|
nb = "0.1"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# TODO: pending https://github.com/stm32-rs/stm32f4xx-hal/pull/125
|
# TODO: pending https://github.com/stm32-rs/stm32f4xx-hal/pull/125
|
||||||
|
|
|
@ -32,6 +32,7 @@ mod init_log;
|
||||||
use init_log::init_log;
|
use init_log::init_log;
|
||||||
mod pins;
|
mod pins;
|
||||||
use pins::Pins;
|
use pins::Pins;
|
||||||
|
mod softspi;
|
||||||
mod ad7172;
|
mod ad7172;
|
||||||
mod ad5680;
|
mod ad5680;
|
||||||
mod net;
|
mod net;
|
||||||
|
@ -89,6 +90,8 @@ fn main() -> ! {
|
||||||
wd.start(WATCHDOG_INTERVAL.ms());
|
wd.start(WATCHDOG_INTERVAL.ms());
|
||||||
wd.feed();
|
wd.feed();
|
||||||
|
|
||||||
|
timer::setup(cp.SYST, clocks);
|
||||||
|
|
||||||
let pins = Pins::setup(
|
let pins = Pins::setup(
|
||||||
clocks, dp.TIM1, dp.TIM3,
|
clocks, dp.TIM1, dp.TIM3,
|
||||||
dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOE, dp.GPIOF, dp.GPIOG,
|
dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOE, dp.GPIOF, dp.GPIOG,
|
||||||
|
@ -98,8 +101,6 @@ fn main() -> ! {
|
||||||
let mut channels = Channels::new(pins);
|
let mut channels = Channels::new(pins);
|
||||||
channels.calibrate_dac_value(0);
|
channels.calibrate_dac_value(0);
|
||||||
|
|
||||||
timer::setup(cp.SYST, clocks);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "generate-hwaddr"))]
|
#[cfg(not(feature = "generate-hwaddr"))]
|
||||||
let hwaddr = EthernetAddress(NET_HWADDR);
|
let hwaddr = EthernetAddress(NET_HWADDR);
|
||||||
#[cfg(feature = "generate-hwaddr")]
|
#[cfg(feature = "generate-hwaddr")]
|
||||||
|
|
44
src/pins.rs
44
src/pins.rs
|
@ -1,6 +1,6 @@
|
||||||
use stm32f4xx_hal::{
|
use stm32f4xx_hal::{
|
||||||
adc::Adc,
|
adc::Adc,
|
||||||
hal::{blocking::spi::Transfer, digital::v2::OutputPin},
|
hal::{blocking::spi::Transfer, digital::v2::{InputPin, OutputPin}},
|
||||||
gpio::{
|
gpio::{
|
||||||
AF5, Alternate, Analog,
|
AF5, Alternate, Analog,
|
||||||
gpioa::*,
|
gpioa::*,
|
||||||
|
@ -20,6 +20,20 @@ use stm32f4xx_hal::{
|
||||||
time::U32Ext,
|
time::U32Ext,
|
||||||
};
|
};
|
||||||
use crate::channel::{Channel0, Channel1};
|
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 {
|
pub trait ChannelPins {
|
||||||
|
@ -58,8 +72,8 @@ impl ChannelPins for Channel1 {
|
||||||
/// SPI peripheral used for communication with the ADC
|
/// SPI peripheral used for communication with the ADC
|
||||||
pub type AdcSpi = Spi<SPI2, (PB10<Alternate<AF5>>, PB14<Alternate<AF5>>, PB15<Alternate<AF5>>)>;
|
pub type AdcSpi = Spi<SPI2, (PB10<Alternate<AF5>>, PB14<Alternate<AF5>>, PB15<Alternate<AF5>>)>;
|
||||||
pub type AdcNss = PB12<Output<PushPull>>;
|
pub type AdcNss = PB12<Output<PushPull>>;
|
||||||
type Dac0Spi = Spi<SPI4, (PE2<Alternate<AF5>>, NoMiso, PE6<Alternate<AF5>>)>;
|
type Dac0Spi = SoftSpi<PE2<Output<PushPull>>, PE6<Output<PushPull>>, DummyInputPin>;
|
||||||
type Dac1Spi = Spi<SPI5, (PF7<Alternate<AF5>>, NoMiso, PF9<Alternate<AF5>>)>;
|
type Dac1Spi = SoftSpi<PF7<Output<PushPull>>, PF9<Output<PushPull>>, DummyInputPin>;
|
||||||
|
|
||||||
pub type TecUMeasAdc = Adc<ADC3>;
|
pub type TecUMeasAdc = Adc<ADC3>;
|
||||||
|
|
||||||
|
@ -196,14 +210,10 @@ impl Pins {
|
||||||
clocks: Clocks, spi4: SPI4,
|
clocks: Clocks, spi4: SPI4,
|
||||||
sclk: PE2<M1>, sync: PE4<M2>, sdin: PE6<M3>
|
sclk: PE2<M1>, sync: PE4<M2>, sdin: PE6<M3>
|
||||||
) -> (Dac0Spi, PE4<Output<PushPull>>) {
|
) -> (Dac0Spi, PE4<Output<PushPull>>) {
|
||||||
let sclk = sclk.into_alternate_af5();
|
let sclk = sclk.into_push_pull_output();
|
||||||
let sdin = sdin.into_alternate_af5();
|
let sdin = sdin.into_push_pull_output();
|
||||||
let spi = Spi::spi4(
|
let spi = SoftSpi::new(
|
||||||
spi4,
|
sclk, sdin, DummyInputPin,
|
||||||
(sclk, NoMiso, sdin),
|
|
||||||
crate::ad5680::SPI_MODE,
|
|
||||||
crate::ad5680::SPI_CLOCK.into(),
|
|
||||||
clocks
|
|
||||||
);
|
);
|
||||||
let sync = sync.into_push_pull_output();
|
let sync = sync.into_push_pull_output();
|
||||||
|
|
||||||
|
@ -214,14 +224,10 @@ impl Pins {
|
||||||
clocks: Clocks, spi5: SPI5,
|
clocks: Clocks, spi5: SPI5,
|
||||||
sclk: PF7<M1>, sync: PF6<M2>, sdin: PF9<M3>
|
sclk: PF7<M1>, sync: PF6<M2>, sdin: PF9<M3>
|
||||||
) -> (Dac1Spi, PF6<Output<PushPull>>) {
|
) -> (Dac1Spi, PF6<Output<PushPull>>) {
|
||||||
let sclk = sclk.into_alternate_af5();
|
let sclk = sclk.into_push_pull_output();
|
||||||
let sdin = sdin.into_alternate_af5();
|
let sdin = sdin.into_push_pull_output();
|
||||||
let spi = Spi::spi5(
|
let spi = SoftSpi::new(
|
||||||
spi5,
|
sclk, sdin, DummyInputPin,
|
||||||
(sclk, NoMiso, sdin),
|
|
||||||
crate::ad5680::SPI_MODE,
|
|
||||||
crate::ad5680::SPI_CLOCK.into(),
|
|
||||||
clocks
|
|
||||||
);
|
);
|
||||||
let sync = sync.into_push_pull_output();
|
let sync = sync.into_push_pull_output();
|
||||||
|
|
||||||
|
|
|
@ -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