mirror of
https://github.com/PSenfft/mb85rc.git
synced 2026-04-30 19:19:09 +00:00
Compare commits
5 Commits
a4e8cc5250
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 60f5cc12e4 | |||
| c6d7ca79d0 | |||
| 54d35ebbec | |||
| 11b10b53e8 | |||
| 478a037fb0 |
3
src/embedded_io.rs
Normal file
3
src/embedded_io.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod device;
|
||||
mod head;
|
||||
mod error;
|
||||
78
src/embedded_io/device.rs
Normal file
78
src/embedded_io/device.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use embedded_hal::i2c::I2c;
|
||||
use embedded_io::{ErrorType, Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
MB85RC,
|
||||
embedded_io::{error::MB85RCErrorType, head::Head},
|
||||
};
|
||||
|
||||
pub struct EmbedIODev<T: I2c, const N: u64> {
|
||||
dev: crate::MB85RC<T>,
|
||||
head: Head<N>,
|
||||
}
|
||||
|
||||
impl<T: I2c, const N: u64> EmbedIODev<T, N> {
|
||||
pub fn new(mb85rc: MB85RC<T>) -> Self {
|
||||
Self {
|
||||
dev: mb85rc,
|
||||
head: Head::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: I2c, const N: u64> ErrorType for EmbedIODev<T, N> {
|
||||
type Error = MB85RCErrorType<T::Error>;
|
||||
}
|
||||
|
||||
impl<T: I2c, const N: u64> Read for EmbedIODev<T, N> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
match self.head.memory_address() {
|
||||
Some(addr) => self
|
||||
.dev
|
||||
.sequential_read(&addr, buf)
|
||||
.map_err(MB85RCErrorType::I2c)
|
||||
.map(|_| {
|
||||
self.head.advance(buf.len());
|
||||
buf.len()
|
||||
}),
|
||||
None => Err(MB85RCErrorType::InvalidPosition),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: I2c, const N: u64> Write for EmbedIODev<T, N> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
// From trait doc: Implementations must not return Ok(0) unless buf is empty.
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// match self.head.memory_address() {
|
||||
// Some(addr) => self
|
||||
// .dev
|
||||
// .write_page(&addr, buf)
|
||||
// .map_err(MB85RCErrorType::I2c)
|
||||
// .map(|_| {
|
||||
// self.head.advance(buf.len());
|
||||
// buf.len()
|
||||
// }),
|
||||
// None => Err(MB85RCErrorType::InvalidPosition),
|
||||
// }
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
// We can't really flush here.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: I2c, const N: u64> Seek for EmbedIODev<T, N> {
|
||||
fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
self.head
|
||||
.seek(pos)
|
||||
.ok_or(MB85RCErrorType::InvalidPosition)?;
|
||||
Ok(self.head.into())
|
||||
}
|
||||
}
|
||||
17
src/embedded_io/error.rs
Normal file
17
src/embedded_io/error.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use embedded_hal::i2c;
|
||||
use embedded_io::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MB85RCErrorType<T: i2c::Error> {
|
||||
I2c(T),
|
||||
InvalidPosition,
|
||||
}
|
||||
|
||||
impl<T: i2c::Error> Error for MB85RCErrorType<T> {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
match self {
|
||||
MB85RCErrorType::I2c(_) => embedded_io::ErrorKind::Other,
|
||||
MB85RCErrorType::InvalidPosition => embedded_io::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
217
src/embedded_io/head.rs
Normal file
217
src/embedded_io/head.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use embedded_io::SeekFrom;
|
||||
|
||||
/// moveable Read/Write head
|
||||
/// Capped at `N`
|
||||
/// Does not allow overflow
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Head<const N: u64>(u64);
|
||||
|
||||
impl<const N: u64> Head<N> {
|
||||
pub fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn seek(&mut self, pos: SeekFrom) -> Option<u64> {
|
||||
match pos {
|
||||
SeekFrom::Start(offset) => match self.0.checked_add(offset).filter(|&sum| sum <= N) {
|
||||
Some(new_pos) => {
|
||||
self.0 = new_pos;
|
||||
Some(new_pos)
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
SeekFrom::End(offset) => {
|
||||
// Do not allow seek over the end
|
||||
if offset > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.0 = N - offset.unsigned_abs();
|
||||
|
||||
Some(self.0)
|
||||
}
|
||||
SeekFrom::Current(offset) => {
|
||||
let new_pos = if offset > 0 {
|
||||
self.0.checked_add(offset.unsigned_abs())
|
||||
} else {
|
||||
self.0.checked_sub(offset.unsigned_abs())
|
||||
};
|
||||
|
||||
match new_pos {
|
||||
Some(pos) => {
|
||||
if pos > N {
|
||||
None
|
||||
} else {
|
||||
self.0 = pos;
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the head forward when reading files.
|
||||
/// Expected to not overflow. If it does it is capped at `N`.
|
||||
pub fn advance(&mut self, bytes: usize) {
|
||||
match self.0.checked_add(bytes as u64) {
|
||||
Some(sum) => {
|
||||
if sum > N {
|
||||
self.0 = N
|
||||
} else {
|
||||
self.0 = sum;
|
||||
}
|
||||
}
|
||||
None => self.0 = N,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to a 2 byte memory address used by the i2c interface.
|
||||
pub fn memory_address(&self) -> Option<[u8; 2]> {
|
||||
if self.0 > u16::MAX as u64 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let addr16 = self.0 as u16;
|
||||
let high = (addr16 >> 8) as u8;
|
||||
let low = (addr16 & 0xFF) as u8;
|
||||
|
||||
Some([high, low])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: u64> From<Head<N>> for u64 {
|
||||
fn from(head: Head<N>) -> Self {
|
||||
head.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let head: Head<65_535> = Head::new();
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 0u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_start() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let res = head.seek(SeekFrom::Start(1337));
|
||||
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 1337u64);
|
||||
assert_eq!(res, Some(inner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_current_forward() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(1337));
|
||||
|
||||
let res = head.seek(SeekFrom::Current(3));
|
||||
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 1340u64);
|
||||
assert_eq!(res, Some(inner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_current_back() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(1337));
|
||||
|
||||
let res = head.seek(SeekFrom::Current(-337));
|
||||
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 1000u64);
|
||||
assert_eq!(res, Some(inner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_current_zero() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(1337));
|
||||
|
||||
let res = head.seek(SeekFrom::Current(0));
|
||||
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 1337u64);
|
||||
assert_eq!(res, Some(inner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_end() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let res = head.seek(SeekFrom::End(-10));
|
||||
|
||||
let inner: u64 = head.into();
|
||||
assert_eq!(inner, 65_535 - 10);
|
||||
assert_eq!(res, Some(inner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_end_overflow() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let res = head.seek(SeekFrom::End(10));
|
||||
assert!(res.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_invalid_overflow() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(65_535 - 5));
|
||||
|
||||
let res = head.seek(SeekFrom::Current(10));
|
||||
assert!(res.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_invalid_underflow() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
|
||||
let res = head.seek(SeekFrom::Current(-1));
|
||||
assert!(res.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_zero() {
|
||||
let head: Head<65_535> = Head::new();
|
||||
let addr = head.memory_address();
|
||||
|
||||
assert_eq!(addr, Some([0, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_1_byte() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(50));
|
||||
|
||||
let addr = head.memory_address();
|
||||
|
||||
assert_eq!(addr, Some([0, 50]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_2_bytes() {
|
||||
let mut head: Head<65_535> = Head::new();
|
||||
let _ = head.seek(SeekFrom::Start(260));
|
||||
|
||||
let addr = head.memory_address();
|
||||
|
||||
assert_eq!(addr, Some([1, 4]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_invalid() {
|
||||
let mut head: Head<4_294_967_295> = Head::new(); // Needs to be more then u16::MAX
|
||||
let _ = head.seek(SeekFrom::Start(65_536)); // One more then u16::MAX
|
||||
|
||||
let addr = head.memory_address();
|
||||
assert!(addr.is_none());
|
||||
}
|
||||
}
|
||||
72
src/lib.rs
72
src/lib.rs
@@ -1,12 +1,19 @@
|
||||
#![no_std]
|
||||
|
||||
//! The `MemoryAddress` are two bit.
|
||||
//! For the 16Kb version 11 bit are used.
|
||||
//! For the 256Kb version 16 bit are used.
|
||||
//! For the 64Kb version 16 bit are used.
|
||||
|
||||
use core::result::Result;
|
||||
use embedded_hal::i2c::{I2c, SevenBitAddress};
|
||||
|
||||
const DEVICE_ADDRESS: u8 = 0b10100000;
|
||||
const DEVICE_ADDRESS_CODE: u8 = 0b00000000;
|
||||
const DEVICE_W: u8 = 0b00000000;
|
||||
const DEVICE_R: u8 = 0b00000001;
|
||||
mod async_hal;
|
||||
mod embedded_io;
|
||||
|
||||
|
||||
/// [High,Low]
|
||||
type MemoryAddress = [u8; 2];
|
||||
|
||||
pub struct MB85RC<T: I2c<SevenBitAddress>> {
|
||||
i2c: T,
|
||||
@@ -20,11 +27,7 @@ impl<T: I2c> MB85RC<T> {
|
||||
|
||||
/// The Device ID command reads fixed Device ID. The size of Device ID is 3 bytes and consists of manufacturer
|
||||
/// ID and product ID.
|
||||
/// # Arguments
|
||||
/// * `self` - A mutable reference to the MB85RC instance.
|
||||
/// # Returns
|
||||
/// * `Result<[u8; 3], Error>` - Device ID is 3 bytes and consists of manufacturer ID and product ID
|
||||
pub fn get_device_id(&mut self) -> Result<[u8; 3], T::Error> {
|
||||
pub fn get_device_id(&mut self) -> Result<[u8; 3], T::Error> {
|
||||
let mut buffer: [u8; 3] = [0, 0, 0];
|
||||
let reserved_slave_address = 0x7C; // Reserved Slave ID F9H without last bit, because wrte address adds this bit
|
||||
let payload = [0xA0]; // Device Address + read bit (write bit works also, because R/W code are “Don't care” value)
|
||||
@@ -35,17 +38,9 @@ impl<T: I2c> MB85RC<T> {
|
||||
}
|
||||
|
||||
/// Write bit on the specified memory address
|
||||
/// # Arguments
|
||||
/// * `self` - A mutable reference to the MB85RC instance.
|
||||
/// * `memory_address` - The memory address to write to.
|
||||
/// * `data` - The data byte to write.
|
||||
/// # Returns
|
||||
/// * `Result<u8, T::Error>`
|
||||
pub fn byte_write(&mut self, memory_address: [u8; 2], data: u8) -> Result<u8, T::Error> {
|
||||
pub fn byte_write(&mut self, memory_address: &MemoryAddress, data: u8) -> Result<(), T::Error> {
|
||||
let payload = [memory_address[0], memory_address[1], data];
|
||||
self.i2c.write(self.address, &payload)?;
|
||||
|
||||
Ok(data)
|
||||
self.i2c.write(self.address, &payload)
|
||||
}
|
||||
|
||||
/// If additional 8 bits are continuously sent after the same command (except stop condition) as Byte Write, a
|
||||
@@ -54,34 +49,21 @@ impl<T: I2c> MB85RC<T> {
|
||||
/// of the memory address that was written first. Because FRAM performs the high-speed write operations, the
|
||||
/// data will be written to FRAM right after the ACK response finished.
|
||||
/// array 32KB
|
||||
/// # Arguments
|
||||
/// * `self` - A mutable reference to the MB85RC instance.
|
||||
/// * `memory_address` - The memory address to write to.
|
||||
/// * `data` - The data bytes to write max 32KB.
|
||||
/// # Returns
|
||||
/// * `Result<(), T::Error>`
|
||||
pub fn write_page(
|
||||
&mut self,
|
||||
memory_address: [u8; 2],
|
||||
data: &mut [u8; 32000],
|
||||
memory_address: &MemoryAddress,
|
||||
data: &[u8],
|
||||
) -> Result<(), T::Error> {
|
||||
let mut payload = [0u8; 32002];
|
||||
payload[0] = memory_address[0];
|
||||
payload[1] = memory_address[1];
|
||||
payload[2..].copy_from_slice(data);
|
||||
self.i2c.write(self.address, &payload)?;
|
||||
|
||||
Ok(())
|
||||
self.i2c.write(self.address, &payload[..2 + data.len()])
|
||||
}
|
||||
|
||||
/// The one byte of data from the memory address saved in the memory address buffer can be read out
|
||||
/// synchronously
|
||||
/// # Arguments
|
||||
/// * `self` - A mutable reference to the MB85RC instance.
|
||||
/// * `memory_address` - The memory address to read from.
|
||||
/// # Returns
|
||||
/// * `Result<u8, Error>` - The byte read from the specified
|
||||
pub fn random_read(&mut self, memory_address: &[u8; 2]) -> Result<u8, T::Error> {
|
||||
pub fn random_read(&mut self, memory_address: &MemoryAddress) -> Result<u8, T::Error> {
|
||||
let mut buffer: [u8; 1] = [0];
|
||||
self.i2c
|
||||
.write_read(self.address, memory_address, &mut buffer)?;
|
||||
@@ -95,19 +77,11 @@ impl<T: I2c> MB85RC<T> {
|
||||
/// command. If the end of the memory address space is reached, the internal read
|
||||
/// address automatically rolls over to the first memory address (0x0000) and continues
|
||||
/// reading.
|
||||
/// # Arguments
|
||||
/// * `self` - A mutable reference to the MB85RC instance.
|
||||
/// * `memory_address` - The memory address to read from.
|
||||
/// * `buffer` - buffer to write the payload data
|
||||
/// # Return
|
||||
/// * `Result<&'a mut [u8], T::Error>` - Pointer to the array with the read data
|
||||
pub fn sequential_read<'a>(
|
||||
pub fn sequential_read(
|
||||
&mut self,
|
||||
memory_address: &[u8; 2],
|
||||
buffer: &'a mut [u8],
|
||||
) -> Result<&'a mut [u8], T::Error> {
|
||||
self.i2c.write_read(self.address, memory_address, buffer)?;
|
||||
|
||||
Ok(buffer)
|
||||
memory_address: &MemoryAddress,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), T::Error> {
|
||||
self.i2c.write_read(self.address, memory_address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user