Compare commits

..

8 Commits

Author SHA1 Message Date
Astro a102e5fcec main: wait for more tcp send headroom 2020-03-21 00:11:23 +01:00
Astro 10f7020c02 main: fix report padding 2020-03-21 00:11:03 +01:00
Astro da65ea21a4 ad7172: eliminate superfluous AdcError 2020-03-21 00:10:12 +01:00
Astro 9c3485d05f timer: increase rate to 500 Hz
timestamp precision: 0.002s
2020-03-21 00:07:18 +01:00
Astro 8e41c44303 adc: use ref_sel=external 2020-03-21 00:06:27 +01:00
Astro 1cba8e56c9 ad7172: add calibrate_offset() 2020-03-21 00:06:09 +01:00
Astro 63cc1d2fe1 main: always do full session reset 2020-03-20 23:20:50 +01:00
Astro 194871c85a ad7172: readback after write_reg() 2020-03-20 23:19:31 +01:00
6 changed files with 97 additions and 66 deletions

View File

@ -2,11 +2,10 @@ use core::fmt;
use embedded_hal::digital::v2::OutputPin; use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::spi::Transfer; use embedded_hal::blocking::spi::Transfer;
use log::{info, warn}; use log::{info, warn};
use super::checksum::{ChecksumMode, Checksum};
use super::AdcError;
use super::{ use super::{
regs::{self, Register, RegisterData}, regs::{self, Register, RegisterData},
Input, RefSource, PostFilter, DigitalFilterOrder, checksum::{ChecksumMode, Checksum},
Mode, Input, RefSource, PostFilter, DigitalFilterOrder,
}; };
/// AD7172-2 implementation /// AD7172-2 implementation
@ -19,7 +18,7 @@ pub struct Adc<SPI: Transfer<u8>, NSS: OutputPin> {
} }
impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> { impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
pub fn new(spi: SPI, mut nss: NSS) -> Result<Self, AdcError<SPI::Error>> { pub fn new(spi: SPI, mut nss: NSS) -> Result<Self, SPI::Error> {
let _ = nss.set_high(); let _ = nss.set_high();
let mut adc = Adc { let mut adc = Adc {
spi, nss, spi, nss,
@ -42,18 +41,19 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
let mut adc_mode = <regs::AdcMode as Register>::Data::empty(); let mut adc_mode = <regs::AdcMode as Register>::Data::empty();
adc_mode.set_ref_en(true); adc_mode.set_ref_en(true);
adc.write_reg(&regs::AdcMode, &mut adc_mode); adc_mode.set_mode(Mode::ContinuousConversion);
adc.write_reg(&regs::AdcMode, &mut adc_mode)?;
Ok(adc) Ok(adc)
} }
/// `0x00DX` for AD7172-2 /// `0x00DX` for AD7172-2
pub fn identify(&mut self) -> Result<u16, AdcError<SPI::Error>> { pub fn identify(&mut self) -> Result<u16, SPI::Error> {
self.read_reg(&regs::Id) self.read_reg(&regs::Id)
.map(|id| id.id()) .map(|id| id.id())
} }
pub fn set_checksum_mode(&mut self, mode: ChecksumMode) -> Result<(), AdcError<SPI::Error>> { pub fn set_checksum_mode(&mut self, mode: ChecksumMode) -> Result<(), SPI::Error> {
// Cannot use update_reg() here because checksum_mode is // Cannot use update_reg() here because checksum_mode is
// updated between read_reg() and write_reg(). // updated between read_reg() and write_reg().
let mut ifmode = self.read_reg(&regs::IfMode)?; let mut ifmode = self.read_reg(&regs::IfMode)?;
@ -63,7 +63,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
Ok(()) Ok(())
} }
pub fn set_sync_enable(&mut self, enable: bool) -> Result<(), AdcError<SPI::Error>> { pub fn set_sync_enable(&mut self, enable: bool) -> Result<(), SPI::Error> {
self.update_reg(&regs::GpioCon, |data| { self.update_reg(&regs::GpioCon, |data| {
data.set_sync_en(enable); data.set_sync_en(enable);
}) })
@ -71,23 +71,20 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
pub fn setup_channel( pub fn setup_channel(
&mut self, index: u8, in_pos: Input, in_neg: Input &mut self, index: u8, in_pos: Input, in_neg: Input
) -> Result<(), AdcError<SPI::Error>> { ) -> Result<(), SPI::Error> {
self.update_reg(&regs::SetupCon { index }, |data| { self.update_reg(&regs::SetupCon { index }, |data| {
data.set_bipolar(false); data.set_bipolar(false);
data.set_refbuf_pos(true); data.set_refbuf_pos(true);
data.set_refbuf_neg(true); data.set_refbuf_neg(true);
data.set_ainbuf_pos(true); data.set_ainbuf_pos(true);
data.set_ainbuf_neg(true); data.set_ainbuf_neg(true);
data.set_ref_sel(RefSource::Internal); data.set_ref_sel(RefSource::External);
})?; })?;
self.update_reg(&regs::FiltCon { index }, |data| { self.update_reg(&regs::FiltCon { index }, |data| {
data.set_enh_filt_en(true); data.set_enh_filt_en(true);
data.set_enh_filt(PostFilter::F16SPS); data.set_enh_filt(PostFilter::F16SPS);
data.set_order(DigitalFilterOrder::Sinc5Sinc1); data.set_order(DigitalFilterOrder::Sinc5Sinc1);
})?; })?;
let mut offset = <regs::Offset as regs::Register>::Data::empty();
offset.set_offset(0);
self.write_reg(&regs::Offset { index }, &mut offset);
self.update_reg(&regs::Channel { index }, |data| { self.update_reg(&regs::Channel { index }, |data| {
data.set_setup(index); data.set_setup(index);
data.set_enabled(true); data.set_enabled(true);
@ -97,7 +94,21 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
Ok(()) Ok(())
} }
pub fn get_postfilter(&mut self, index: u8) -> Result<Option<PostFilter>, AdcError<SPI::Error>> { /// Calibrates offset registers
pub fn calibrate_offset(&mut self) -> Result<(), SPI::Error> {
self.update_reg(&regs::AdcMode, |adc_mode| {
adc_mode.set_mode(Mode::SystemOffsetCalibration);
})?;
while ! self.read_reg(&regs::Status)?.ready() {}
self.update_reg(&regs::AdcMode, |adc_mode| {
adc_mode.set_mode(Mode::ContinuousConversion);
})?;
Ok(())
}
pub fn get_postfilter(&mut self, index: u8) -> Result<Option<PostFilter>, SPI::Error> {
self.read_reg(&regs::FiltCon { index }) self.read_reg(&regs::FiltCon { index })
.map(|data| { .map(|data| {
if data.enh_filt_en() { if data.enh_filt_en() {
@ -108,7 +119,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
}) })
} }
pub fn set_postfilter(&mut self, index: u8, filter: Option<PostFilter>) -> Result<(), AdcError<SPI::Error>> { pub fn set_postfilter(&mut self, index: u8, filter: Option<PostFilter>) -> Result<(), SPI::Error> {
self.update_reg(&regs::FiltCon { index }, |data| { self.update_reg(&regs::FiltCon { index }, |data| {
match filter { match filter {
None => data.set_enh_filt_en(false), None => data.set_enh_filt_en(false),
@ -121,7 +132,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
} }
/// Returns the channel the data is from /// Returns the channel the data is from
pub fn data_ready(&mut self) -> Result<Option<u8>, AdcError<SPI::Error>> { pub fn data_ready(&mut self) -> Result<Option<u8>, SPI::Error> {
self.read_reg(&regs::Status) self.read_reg(&regs::Status)
.map(|status| { .map(|status| {
if status.ready() { if status.ready() {
@ -133,12 +144,12 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
} }
/// Get data /// Get data
pub fn read_data(&mut self) -> Result<u32, AdcError<SPI::Error>> { pub fn read_data(&mut self) -> Result<u32, SPI::Error> {
self.read_reg(&regs::Data) self.read_reg(&regs::Data)
.map(|data| data.data()) .map(|data| data.data())
} }
fn read_reg<R: regs::Register>(&mut self, reg: &R) -> Result<R::Data, AdcError<SPI::Error>> { fn read_reg<R: regs::Register>(&mut self, reg: &R) -> Result<R::Data, SPI::Error> {
let mut reg_data = R::Data::empty(); let mut reg_data = R::Data::empty();
let address = 0x40 | reg.address(); let address = 0x40 | reg.address();
let mut checksum = Checksum::new(self.checksum_mode); let mut checksum = Checksum::new(self.checksum_mode);
@ -154,12 +165,13 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
break; break;
} }
// Retry // Retry
warn!("read_reg checksum error, retrying"); warn!("read_reg {:02X}: checksum error: {:?}!={:?}, retrying", reg.address(), checksum_expected, checksum_in);
} }
Ok(reg_data) Ok(reg_data)
} }
fn write_reg<R: regs::Register>(&mut self, reg: &R, reg_data: &mut R::Data) -> Result<(), AdcError<SPI::Error>> { fn write_reg<R: regs::Register>(&mut self, reg: &R, reg_data: &mut R::Data) -> Result<(), SPI::Error> {
loop {
let address = reg.address(); let address = reg.address();
let mut checksum = Checksum::new(match self.checksum_mode { let mut checksum = Checksum::new(match self.checksum_mode {
ChecksumMode::Off => ChecksumMode::Off, ChecksumMode::Off => ChecksumMode::Off,
@ -170,17 +182,19 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
checksum.feed(&[address]); checksum.feed(&[address]);
checksum.feed(&reg_data); checksum.feed(&reg_data);
let checksum_out = checksum.result(); let checksum_out = checksum.result();
loop {
let checksum_in = self.transfer(address, reg_data.as_mut(), checksum_out)?; let mut data = reg_data.clone();
if checksum_in.unwrap_or(0) == 0 { let checksum_in = self.transfer(address, data.as_mut(), checksum_out)?;
break;
let readback_data = self.read_reg(reg)?;
if *readback_data == **reg_data {
return Ok(());
} }
warn!("write_reg: checksum={:02X}, retrying", checksum_in.unwrap_or(0)); warn!("write_reg {:02X}: readback error, {:?}!={:?}, retrying", address, &*readback_data, &**reg_data);
} }
Ok(())
} }
fn update_reg<R, F, A>(&mut self, reg: &R, f: F) -> Result<A, AdcError<SPI::Error>> fn update_reg<R, F, A>(&mut self, reg: &R, f: F) -> Result<A, SPI::Error>
where where
R: regs::Register, R: regs::Register,
F: FnOnce(&mut R::Data) -> A, F: FnOnce(&mut R::Data) -> A,

View File

@ -21,15 +21,33 @@ pub const SPI_CLOCK: MegaHertz = MegaHertz(2);
pub const MAX_VALUE: u32 = 0xFF_FFFF; pub const MAX_VALUE: u32 = 0xFF_FFFF;
#[derive(Clone, Debug, PartialEq)]
pub enum AdcError<SPI> { #[derive(Clone, Copy, Debug)]
SPI(SPI), #[repr(u8)]
ChecksumMismatch(Option<u8>, Option<u8>), pub enum Mode {
ContinuousConversion = 0b000,
SingleConversion = 0b001,
Standby = 0b010,
PowerDown = 0b011,
InternalOffsetCalibration = 0b100,
Invalid,
SystemOffsetCalibration = 0b110,
SystemGainCalibration = 0b111,
} }
impl<SPI> From<SPI> for AdcError<SPI> { impl From<u8> for Mode {
fn from(e: SPI) -> Self { fn from(x: u8) -> Self {
AdcError::SPI(e) use Mode::*;
match x {
0b000 => ContinuousConversion,
0b001 => SingleConversion,
0b010 => Standby,
0b011 => PowerDown,
0b100 => InternalOffsetCalibration,
0b110 => SystemOffsetCalibration,
0b111 => SystemGainCalibration,
_ => Invalid,
}
} }
} }

View File

@ -8,7 +8,8 @@ pub trait Register {
type Data: RegisterData; type Data: RegisterData;
fn address(&self) -> u8; fn address(&self) -> u8;
} }
pub trait RegisterData: Deref<Target=[u8]> + DerefMut {
pub trait RegisterData: Clone + Deref<Target=[u8]> + DerefMut {
fn empty() -> Self; fn empty() -> Self;
} }
@ -26,6 +27,7 @@ macro_rules! def_reg {
} }
mod $reg { mod $reg {
/// Register contents /// Register contents
#[derive(Clone)]
pub struct Data(pub [u8; $size]); pub struct Data(pub [u8; $size]);
impl super::RegisterData for Data { impl super::RegisterData for Data {
/// Generate zeroed register contents /// Generate zeroed register contents
@ -55,6 +57,7 @@ macro_rules! def_reg {
} }
} }
mod $reg { mod $reg {
#[derive(Clone)]
pub struct Data(pub [u8; $size]); pub struct Data(pub [u8; $size]);
impl super::RegisterData for Data { impl super::RegisterData for Data {
fn empty() -> Self { fn empty() -> Self {
@ -155,12 +158,12 @@ impl status::Data {
def_reg!(AdcMode, adc_mode, 0x01, 2); def_reg!(AdcMode, adc_mode, 0x01, 2);
impl adc_mode::Data { impl adc_mode::Data {
reg_bits!(clockset, set_clocksel, 1, 2..3, "Clock source"); reg_bits!(delay, set_delay, 0, 0..=2, "Delay after channel switch");
reg_bits!(mode, set_mode, 1, 4..6, "Operating mode"); reg_bit!(sing_cyc, set_sing_cyc, 0, 5, "Can only used with single channel");
reg_bits!(delay, set_delay, 0, 0..2, "Delay after channel switch"); reg_bit!(hide_delay, set_hide_delay, 0, 6, "Hide delay");
reg_bit!(sing_cyc, set_sing_cyc, 1, 5, "Can only used with single channel");
reg_bit!(hide_delay, set_hide_delay, 1, 6, "Hide delay");
reg_bit!(ref_en, set_ref_en, 0, 7, "Enable internal reference, output buffered 2.5 V to REFOUT"); reg_bit!(ref_en, set_ref_en, 0, 7, "Enable internal reference, output buffered 2.5 V to REFOUT");
reg_bits!(clockset, set_clocksel, 1, 2..=3, "Clock source");
reg_bits!(mode, set_mode, 1, 4..=6, Mode, "Operating mode");
} }
def_reg!(IfMode, if_mode, 0x02, 2); def_reg!(IfMode, if_mode, 0x02, 2);

View File

@ -94,6 +94,7 @@ fn main() -> ! {
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap(); let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap(); adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap(); adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
adc.calibrate_offset().unwrap();
let mut dac0 = ad5680::Dac::new(pins.dac0_spi, pins.dac0_sync); let mut dac0 = ad5680::Dac::new(pins.dac0_spi, pins.dac0_sync);
dac0.set(0).unwrap(); dac0.set(0).unwrap();
let mut dac1 = ad5680::Dac::new(pins.dac1_spi, pins.dac1_sync); let mut dac1 = ad5680::Dac::new(pins.dac1_spi, pins.dac1_sync);
@ -118,14 +119,14 @@ fn main() -> ! {
net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| { net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
Server::<Session>::run(iface, |server| { Server::<Session>::run(iface, |server| {
loop { loop {
let now = timer::now().0; let instant = Instant::from_millis(i64::from(timer::now()));
let instant = Instant::from_millis(i64::from(now));
cortex_m::interrupt::free(net::clear_pending); cortex_m::interrupt::free(net::clear_pending);
server.poll(instant) server.poll(instant)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
warn!("poll: {:?}", e); warn!("poll: {:?}", e);
}); });
let instant = Instant::from_millis(i64::from(timer::now()));
// ADC input // ADC input
adc.data_ready().unwrap().map(|channel| { adc.data_ready().unwrap().map(|channel| {
let data = adc.read_data().unwrap(); let data = adc.read_data().unwrap();
@ -152,10 +153,8 @@ fn main() -> ! {
server.for_each(|mut socket, session| { server.for_each(|mut socket, session| {
if ! socket.is_open() { if ! socket.is_open() {
let _ = socket.listen(TCP_PORT); let _ = socket.listen(TCP_PORT);
if session.is_dirty() {
session.reset(); session.reset();
} } else if socket.can_send() && socket.can_recv() && socket.send_capacity() - socket.send_queue() > 1024 {
} else if socket.can_send() && socket.can_recv() && socket.send_capacity() - socket.send_queue() > 128 {
match socket.recv(|buf| session.feed(buf)) { match socket.recv(|buf| session.feed(buf)) {
Ok(SessionOutput::Nothing) => {} Ok(SessionOutput::Nothing) => {}
Ok(SessionOutput::Command(command)) => match command { Ok(SessionOutput::Command(command)) => match command {
@ -391,7 +390,7 @@ fn main() -> ! {
while let Some(channel) = session.is_report_pending() { while let Some(channel) = session.is_report_pending() {
let state = &mut channel_states[usize::from(channel)]; let state = &mut channel_states[usize::from(channel)];
let _ = writeln!( let _ = writeln!(
socket, "t={} raw{}=0x{:04X}", socket, "t={} raw{}=0x{:06X}",
state.adc_time, channel, state.adc_data.unwrap_or(0) state.adc_time, channel, state.adc_data.unwrap_or(0)
).map(|_| { ).map(|_| {
session.mark_report_sent(channel); session.mark_report_sent(channel);

View File

@ -79,10 +79,6 @@ impl Session {
self.report_pending = [false; CHANNELS]; self.report_pending = [false; CHANNELS];
} }
pub fn is_dirty(&self) -> bool {
self.reader.pos > 0
}
pub fn reporting(&self) -> bool { pub fn reporting(&self) -> bool {
self.reporting self.reporting
} }

View File

@ -1,15 +1,16 @@
use core::cell::RefCell; use core::cell::RefCell;
use core::ops::Deref;
use cortex_m::interrupt::Mutex; use cortex_m::interrupt::Mutex;
use cortex_m_rt::exception; use cortex_m_rt::exception;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
rcc::Clocks, rcc::Clocks,
time::{U32Ext, MilliSeconds}, time::U32Ext,
timer::{Timer, Event as TimerEvent}, timer::{Timer, Event as TimerEvent},
stm32::SYST, stm32::SYST,
}; };
/// Rate in Hz /// Rate in Hz
const TIMER_RATE: u32 = 20; const TIMER_RATE: u32 = 500;
/// Interval duration in milliseconds /// Interval duration in milliseconds
const TIMER_DELTA: u32 = 1000 / TIMER_RATE; const TIMER_DELTA: u32 = 1000 / TIMER_RATE;
/// Elapsed time in milliseconds /// Elapsed time in milliseconds
@ -31,10 +32,10 @@ fn SysTick() {
} }
/// Obtain current time in milliseconds /// Obtain current time in milliseconds
pub fn now() -> MilliSeconds { pub fn now() -> u32 {
let ms = cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs) *TIMER_MS.borrow(cs)
.borrow() .borrow()
}); .deref()
ms.ms() })
} }