use super::*;
use bit_field::BitField;
use core::convert::TryFrom;
use hal::blocking::spi::{Transfer, Write};
use hal::digital::v2::OutputPin;
pub trait DeviceType {
const ADDRESS_BYTES: usize;
const HOLD_PIN: bool;
const HOLD_STATUS: bool;
const MAX: u32;
fn fill_address(address: &mut u32, instruction: Instruction);
}
macro_rules! impl_device_type {
($devicetype:ident, $a:expr, $b:expr, $c:expr, $d:expr) => {
impl DeviceType for device_type::$devicetype {
const ADDRESS_BYTES: usize = $a;
const HOLD_PIN: bool = $b;
const HOLD_STATUS: bool = $c;
const MAX: u32 = $d;
fn fill_address(address: &mut u32, instruction: Instruction) {
address.set_bits(24..31, instruction as u32);
}
}
};
}
impl_device_type!(M23x640, 3, true, true, 0x1FFF_u32);
impl_device_type!(M23x256, 3, true, true, 0x7FFF_u32);
impl_device_type!(M23x512, 3, true, false, 0xFFFF_u32);
impl_device_type!(M23xv512, 3, false, false, 0xFFFF_u32);
impl_device_type!(M23x1024, 4, true, false, 0x1FFFF_u32);
impl_device_type!(M23xv1024, 4, false, false, 0x1FFFF_u32);
impl<SPI, S, P, CS, HOLD, DT> Sram23x<SPI, CS, HOLD, DT>
where
SPI: Transfer<u8, Error = S> + Write<u8, Error = S>,
CS: OutputPin<Error = P>,
HOLD: OutputPin<Error = P>,
DT: DeviceType,
{
pub fn new(spi: SPI, cs: CS, hold: HOLD, dt: DT) -> Result<Self, Error<S, P>> {
let mut sram = Sram23x {
spi,
cs,
hold,
dt,
mode: 0,
};
sram.cs.set_high().map_err(Error::PinError)?;
sram.set_hold(false)?;
sram.get_mode()?;
Ok(sram)
}
pub fn transfer(&mut self, bytes: &mut [u8]) -> SpiRes<S, P> {
self.cs.set_low().map_err(Error::PinError)?;
self.spi.transfer(bytes).map_err(Error::SpiError)?;
self.cs.set_high().map_err(Error::PinError)?;
Ok(())
}
pub fn get_mode(&mut self) -> Result<u8, Error<S, P>> {
let mut buf: [u8; 2] = [Instruction::ReadMode as u8, 0];
self.transfer(&mut buf)?;
match buf[1].get_bits(6..8) {
0b00 | 0b10 | 0b01 | 0b11 => (),
_ => return Err(Error::UnknownOperatingMode),
};
self.mode = buf[1];
Ok(buf[1])
}
pub fn set_mode(&mut self, mode: u8) -> SpiRes<S, P> {
let mut buf: [u8; 2] = [Instruction::WriteMode as u8, mode];
self.transfer(&mut buf)?;
self.mode = mode;
Ok(())
}
pub fn set_hold(&mut self, enabled: bool) -> SpiRes<S, P> {
if DT::HOLD_PIN {
if enabled {
self.enable_hold_feature()?;
self.hold.set_low().map_err(Error::PinError)
} else {
self.hold.set_high().map_err(Error::PinError)
}
} else {
Ok(())
}
}
pub fn enable_hold_feature(&mut self) -> SpiRes<S, P> {
if DT::HOLD_PIN && DT::HOLD_STATUS {
self.mode.set_bit(0, false);
self.set_mode(self.mode)?;
}
Ok(())
}
pub fn disable_hold_feature(&mut self) -> SpiRes<S, P> {
if DT::HOLD_PIN && DT::HOLD_STATUS {
self.mode.set_bit(0, true);
self.set_mode(self.mode)?;
}
Ok(())
}
pub fn read_byte(&mut self, address: u32) -> Result<u8, Error<S, P>> {
if address > DT::MAX {
Err(Error::InvalidAddress)
} else {
let mut addr = address;
DT::fill_address(&mut addr, Instruction::Read);
let data = addr.to_be_bytes();
let mut buf: [u8; 5] = match DT::ADDRESS_BYTES {
3 => [data[0], data[2], data[3], 0, 0],
4 => [data[0], data[1], data[2], data[3], 0],
_ => return Err(Error::InvalidAddressSize),
};
self.transfer(&mut buf[..=DT::ADDRESS_BYTES])?;
Ok(buf[DT::ADDRESS_BYTES])
}
}
pub fn write_byte(&mut self, address: u32, byte: u8) -> SpiRes<S, P> {
if address > DT::MAX {
Err(Error::InvalidAddress)
} else {
let mut addr = address;
DT::fill_address(&mut addr, Instruction::Write);
let data = addr.to_be_bytes();
let mut buf: [u8; 5] = match DT::ADDRESS_BYTES {
3 => [data[0], data[2], data[3], byte, 0],
4 => [data[0], data[1], data[2], data[3], byte],
_ => return Err(Error::InvalidAddressSize),
};
self.transfer(&mut buf[..=DT::ADDRESS_BYTES])?;
Ok(())
}
}
pub fn read_page(&mut self, address: u32) -> Result<[u8; 32], Error<S, P>> {
if address > DT::MAX {
Err(Error::InvalidAddress)
} else {
let mut addr = address;
DT::fill_address(&mut addr, Instruction::Read);
let data = addr.to_be_bytes();
let mut buf: [u8; 36] = [0; 36];
let size: usize = DT::ADDRESS_BYTES + 32;
let res: [u8; 32] = match DT::ADDRESS_BYTES {
3 => {
buf[0] = data[0];
buf[1] = data[2];
buf[2] = data[3];
self.transfer(&mut buf[..size])?;
TryFrom::try_from(&buf[3..35]).unwrap()
}
4 => {
buf[..4].clone_from_slice(&data[..]);
self.transfer(&mut buf[..size])?;
TryFrom::try_from(&buf[4..]).unwrap()
}
_ => return Err(Error::InvalidAddressSize),
};
Ok(res)
}
}
pub fn write_page(&mut self, address: u32, bytes: &[u8]) -> SpiRes<S, P> {
if address > DT::MAX {
Err(Error::InvalidAddress)
} else if bytes.len() > 32 {
Err(Error::TooMuchData)
} else {
let mut addr = address;
DT::fill_address(&mut addr, Instruction::Write);
let data = addr.to_be_bytes();
let mut buf: [u8; 36] = [0; 36];
match DT::ADDRESS_BYTES {
3 => {
buf[0] = data[0];
buf[1] = data[2];
buf[2] = data[3];
buf[3..35].clone_from_slice(bytes);
}
4 => {
buf[..4].clone_from_slice(&data[..]);
buf[4..].clone_from_slice(bytes);
}
_ => return Err(Error::InvalidAddressSize),
};
let size: usize = DT::ADDRESS_BYTES + 32;
self.transfer(&mut buf[..size])?;
Ok(())
}
}
pub fn read_sequential(&mut self, address: u32, bytes: &mut [u8]) -> SpiRes<S, P> {
self.sequential(address, bytes, Instruction::Read)
}
pub fn write_sequential(&mut self, address: u32, bytes: &mut [u8]) -> SpiRes<S, P> {
self.sequential(address, bytes, Instruction::Write)
}
fn sequential(
&mut self,
address: u32,
bytes: &mut [u8],
instruction: Instruction,
) -> SpiRes<S, P> {
if address > DT::MAX {
Err(Error::InvalidAddress)
} else {
let mut addr = address;
DT::fill_address(&mut addr, instruction);
let data = addr.to_be_bytes();
let mut buf: [u8; 4] = match DT::ADDRESS_BYTES {
3 => [data[0], data[2], data[3], 0],
4 => data,
_ => return Err(Error::InvalidAddressSize),
};
self.cs.set_low().map_err(Error::PinError)?;
self.spi
.transfer(&mut buf[..DT::ADDRESS_BYTES])
.map_err(Error::SpiError)?;
self.spi.transfer(&mut bytes[..]).map_err(Error::SpiError)?;
self.cs.set_high().map_err(Error::PinError)?;
Ok(())
}
}
}