migrate ad5680 to softspi

This commit is contained in:
Astro 2020-05-27 23:16:22 +02:00
parent ec6aa03dc3
commit f323c1be63
5 changed files with 177 additions and 21 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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

View File

@ -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")]

View File

@ -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();

147
src/softspi.rs Normal file
View 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 {}
}