Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions firmware/handheld/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ cfg-if = "1.0"
base64 = "0.22.1"
hex = "0.4.3"
heatshrink-lib = { version = "1.0.0", features = ["embedded-io"] }
# Same family the heatshrink decoder already speaks; the adapters bridge the
# std side until the filesystem itself is abstracted.
embedded-io = "0.6"
embedded-io-adapters = { version = "0.6.0", features = ["std"] }

[build-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions firmware/handheld/src/bitstream/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fs::File;
use std::io::Read;

use embedded_io::Read;
use std::sync::{Mutex, MutexGuard};
use std::time::Duration;

Expand Down Expand Up @@ -91,8 +92,7 @@ fn heatshrink_decompress_stream(file: File) -> impl Read {
// Heatshrink decoder parameters: W=9, L=6 (chosen empirically)
type HeatshrinkDecoder = heatshrink::decoder::HeatshrinkDecoder<9, 6, 512, 512>;
let reader = embedded_io_adapters::std::FromStd::new(file);
let decoder = heatshrink::io::DecoderReader::<_, HeatshrinkDecoder>::new(reader);
embedded_io_adapters::std::ToStd::new(decoder)
heatshrink::io::DecoderReader::<_, HeatshrinkDecoder>::new(reader)
}

pub enum CurrentBitstream {
Expand Down
32 changes: 18 additions & 14 deletions firmware/handheld/src/device/drivers/bq27427.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![allow(dead_code)]

use core::time::Duration;
use embedded_hal::i2c::I2c;
use std::time::Duration;

use super::timer::Timer;
use thiserror::Error;

const ADDRESS: u8 = 0x55;
Expand Down Expand Up @@ -120,16 +122,18 @@ pub enum Error {
InvalidArgument,
}

pub struct BQ27427<I2C: I2c> {
pub struct BQ27427<I2C: I2c, T: Timer> {
i2c: I2C,
timer: T,
}

impl<I2C> BQ27427<I2C>
impl<I2C, T> BQ27427<I2C, T>
where
I2C: I2c,
T: Timer,
{
pub fn new(i2c: I2C) -> Self {
BQ27427 { i2c }
pub fn new(i2c: I2C, timer: T) -> Self {
BQ27427 { i2c, timer }
}

/// Configure the fuel gauge (blocking, may take a while)
Expand Down Expand Up @@ -191,7 +195,7 @@ where
let retries = 10;
let delay = Duration::from_millis(500);
for _ in 0..retries {
std::thread::sleep(delay);
self.timer.sleep(delay);

if (self.get_flags()? & flag) != 0 {
return Ok(());
Expand Down Expand Up @@ -256,7 +260,7 @@ where
self.i2c
.write_read(ADDRESS, &[command.0], &mut data)
.map_err(|_| Error::I2cError)?;
std::thread::sleep(WAIT_TIME);
self.timer.sleep(WAIT_TIME);
Ok(u16::from_le_bytes(data))
}

Expand All @@ -266,7 +270,7 @@ where
self.i2c
.write_read(ADDRESS, &[0x00], &mut data)
.map_err(|_| Error::I2cError)?;
std::thread::sleep(WAIT_TIME);
self.timer.sleep(WAIT_TIME);
Ok(u16::from_le_bytes(data))
}

Expand All @@ -276,11 +280,11 @@ where
self.i2c
.write(ADDRESS, &[0x00, a0])
.map_err(|_| Error::I2cError)?;
std::thread::sleep(WAIT_TIME);
self.timer.sleep(WAIT_TIME);
self.i2c
.write(ADDRESS, &[0x01, a1])
.map_err(|_| Error::I2cError)?;
std::thread::sleep(WAIT_TIME);
self.timer.sleep(WAIT_TIME);
Ok(())
}

Expand Down Expand Up @@ -308,7 +312,7 @@ where
.write(ADDRESS, &[CMD_DATA_BLOCK, offset / 32])
.map_err(|_| Error::I2cError)?;

std::thread::sleep(BLOCK_DELAY);
self.timer.sleep(BLOCK_DELAY);

// Write the bytes to the BlockData
for (i, &x) in data.iter().enumerate() {
Expand All @@ -324,7 +328,7 @@ where
.write(ADDRESS, &[CMD_BLOCK_DATA_CHECKSUM, new_checksum])
.map_err(|_| Error::I2cError)?;

std::thread::sleep(BLOCK_DELAY);
self.timer.sleep(BLOCK_DELAY);
Ok(())
}

Expand Down Expand Up @@ -358,7 +362,7 @@ where
self.i2c
.write(ADDRESS, &[CMD_DATA_BLOCK, offset / 32])
.map_err(|_| Error::I2cError)?;
std::thread::sleep(BLOCK_DELAY);
self.timer.sleep(BLOCK_DELAY);

// Read CC Gain
let mut value = [0u8];
Expand Down Expand Up @@ -389,7 +393,7 @@ where
.write(ADDRESS, &[CMD_BLOCK_DATA_CHECKSUM, new_checksum])
.map_err(|_| Error::I2cError)?;

std::thread::sleep(BLOCK_DELAY);
self.timer.sleep(BLOCK_DELAY);
Ok(())
}
}
17 changes: 11 additions & 6 deletions firmware/handheld/src/device/drivers/dac.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![allow(dead_code)]

use std::time::Duration;
use core::time::Duration;

use super::timer::Timer;

use embedded_hal::digital::OutputPin;
use embedded_hal::i2c::I2c;
Expand Down Expand Up @@ -32,24 +34,27 @@ pub struct InterruptStatus {
pub right_dac_power: bool,
}

pub struct TLV320DAC3101<PinReset: OutputPin, I2C: I2c> {
pub struct TLV320DAC3101<PinReset: OutputPin, I2C: I2c, T: Timer> {
pin_reset: PinReset,
i2c: I2C,
timer: T,
page: u8,

volume: u8,
mute: bool,
}

impl<PinReset, I2C> TLV320DAC3101<PinReset, I2C>
impl<PinReset, I2C, T> TLV320DAC3101<PinReset, I2C, T>
where
PinReset: OutputPin,
I2C: I2c,
T: Timer,
{
pub fn new(pin_reset: PinReset, i2c: I2C) -> Self {
pub fn new(pin_reset: PinReset, i2c: I2C, timer: T) -> Self {
TLV320DAC3101 {
pin_reset,
i2c,
timer,
page: 0,

volume: 0,
Expand All @@ -60,9 +65,9 @@ where
/// Reset the device without configuring it.
pub fn reset(&mut self) -> Result<(), Error> {
self.pin_reset.set_low().map_err(|_| Error::PinError)?;
std::thread::sleep(Duration::from_micros(1));
self.timer.sleep(Duration::from_micros(1));
self.pin_reset.set_high().map_err(|_| Error::PinError)?;
std::thread::sleep(Duration::from_millis(1));
self.timer.sleep(Duration::from_millis(1));
Ok(())
}

Expand Down
36 changes: 21 additions & 15 deletions firmware/handheld/src/device/drivers/fpga/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#![allow(dead_code)]

use std::{
io::Read,
time::{Duration, Instant},
};
use core::time::Duration;

use embedded_io::Read;

use crate::device::drivers::timer::Timer;
use embedded_hal::{
digital::{InputPin, OutputPin},
spi::SpiDevice,
Expand Down Expand Up @@ -77,6 +77,7 @@ pub struct Fpga<
PinProgramB: OutputPin,
PinInitB: InputPin,
ProgramSpi: SpiDevice,
T: Timer,
> {
pin_done: PinDone,
pub pin_program_b: PinProgramB,
Expand All @@ -91,24 +92,29 @@ pub struct Fpga<

/// Bitfield of enabled interrupts
interrupts: u32,

timer: T,
}

impl<'a, PinDone, PinProgramB, PinInitB, ProgramSpi>
Fpga<'a, PinDone, PinProgramB, PinInitB, ProgramSpi>
impl<'a, PinDone, PinProgramB, PinInitB, ProgramSpi, T>
Fpga<'a, PinDone, PinProgramB, PinInitB, ProgramSpi, T>
where
PinDone: InputPin,
PinProgramB: OutputPin,
PinInitB: InputPin,
ProgramSpi: SpiDevice,
T: Timer,
{
pub fn new(
pin_done: PinDone,
pin_program_b: PinProgramB,
pin_init_b: PinInitB,
data_spi: Vec<(SpiDataDriver<'a>, Hertz)>,
program_spi: ProgramSpi,
timer: T,
) -> Self {
Fpga {
timer,
pin_done,
pin_program_b,
pin_init_b,
Expand All @@ -120,9 +126,9 @@ where
}

/// Program the FPGA with a new bitstream.
pub fn program(
pub fn program<R: Read>(
&mut self,
bitstream: &mut dyn Read,
bitstream: &mut R,
scratch_buf: &mut [u8],
) -> Result<(), Error> {
let header =
Expand All @@ -143,30 +149,30 @@ where
// After power-on-reset, INIT_B will be low for 10ms to 35ms (T_POR),
// configuration can only start after this.
// Poll INIT_B until it goes high.
let start_time = Instant::now();
let start_ms = self.timer.now_ms();
while self.pin_init_b.is_low().map_err(|_| Error::PinError)? {
if start_time.elapsed() > Duration::from_millis(35) {
if self.timer.now_ms().saturating_sub(start_ms) > 35 {
return Err(Error::ProgramError);
}
std::thread::sleep(Duration::from_millis(5));
self.timer.sleep(Duration::from_millis(5));
}

// Pull PROGRAM_B low, hold it for at least 250ns.
self.pin_program_b.set_low().map_err(|_| Error::PinError)?;
std::thread::sleep(Duration::from_millis(1));
self.timer.sleep(Duration::from_millis(1));
if self.pin_init_b.is_high().map_err(|_| Error::PinError)? {
return Err(Error::ProgramError);
}
self.pin_program_b.set_high().map_err(|_| Error::PinError)?;

// INIT_B will go high at most 5ms after PROGRAM_B release.
std::thread::sleep(Duration::from_millis(5));
self.timer.sleep(Duration::from_millis(5));
if self.pin_init_b.is_low().map_err(|_| Error::PinError)? {
return Err(Error::ProgramError);
}

log::info!("FPGA is in program mode");
let start_time = Instant::now();
let start_ms = self.timer.now_ms();

let mut num_read = 0;
while num_read < header.length {
Expand All @@ -185,7 +191,7 @@ where
log::info!(
"Programmed FPGA, done={}, time={}",
self.pin_done.is_high().map_err(|_| Error::PinError)?,
start_time.elapsed().as_millis() as u32,
self.timer.now_ms().saturating_sub(start_ms),
);

Ok(())
Expand Down
49 changes: 39 additions & 10 deletions firmware/handheld/src/device/drivers/fpga/xilinx.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::io::Read;

use anyhow::bail;
use embedded_io::{Read, ReadExactError};

#[allow(unused)]
mod consts {
Expand All @@ -11,45 +9,76 @@ mod consts {
pub const TAG_BITSTREAM: u8 = 0x65;
}

const HEADER_LEN: usize = 9;

pub struct BitstreamMetadata {
/// Bitstream payload length
pub length: usize,
/// Vivado UserID
pub user_id: Option<u32>,
}

/// Why a header failed to parse.
///
/// Concrete rather than `anyhow`, which would need the reader's error to be
/// `Send + Sync + 'static` -- a bound `embedded_io::Read` never promises, and
/// one that spreads to every caller until it reaches a reader too opaque to
/// name it on.
#[derive(Debug, thiserror::Error)]
pub enum HeaderError {
#[error("bitstream ended mid-header")]
UnexpectedEof,
#[error("could not read bitstream")]
Io,
#[error("header length field was not {HEADER_LEN}")]
BadHeaderLength,
#[error("header magic did not match")]
BadMagic,
#[error("version field was not 1")]
BadVersion,
}

// Blanket over the reader's error type, so `?` needs no bound on it at all.
impl<E> From<ReadExactError<E>> for HeaderError {
fn from(e: ReadExactError<E>) -> Self {
match e {
ReadExactError::UnexpectedEof => Self::UnexpectedEof,
ReadExactError::Other(_) => Self::Io,
}
}
}

/// Parse the Xilinx .bit header from the file.
///
/// On success, leaves the cursor at the start of the bitstream payload.
pub fn parse_bitstream_header(f: &mut dyn Read) -> anyhow::Result<BitstreamMetadata> {
fn read_u16(f: &mut dyn Read) -> anyhow::Result<u16> {
pub fn parse_bitstream_header<R: Read>(f: &mut R) -> Result<BitstreamMetadata, HeaderError> {
fn read_u16<R: Read>(f: &mut R) -> Result<u16, HeaderError> {
let mut data = [0u8; 2];
f.read_exact(&mut data)?;
Ok(u16::from_be_bytes(data))
}

fn read_u32(f: &mut dyn Read) -> anyhow::Result<u32> {
fn read_u32<R: Read>(f: &mut R) -> Result<u32, HeaderError> {
let mut data = [0u8; 4];
f.read_exact(&mut data)?;
Ok(u32::from_be_bytes(data))
}

// Read initial header
const HEADER_LEN: usize = 9;
let header_len = read_u16(f)?;
if (header_len as usize) != HEADER_LEN {
bail!("Unexpected header length");
return Err(HeaderError::BadHeaderLength);
}
let mut header = [0u8; HEADER_LEN];
f.read_exact(&mut header)?;
if header != [0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x00] {
bail!("Invalid header");
return Err(HeaderError::BadMagic);
}

// Read the 2 bytes (0x0001)... a version perhaps?
let unknown = read_u16(f)?;
if unknown != 1 {
bail!("Invalid unknown value");
return Err(HeaderError::BadVersion);
}

let mut metadata = BitstreamMetadata {
Expand Down
Loading