Skip to content

Commit

Permalink
Merge pull request #51 from BridgeSource/eeprom
Browse files Browse the repository at this point in the history
  • Loading branch information
thejpster authored May 20, 2023
2 parents 758574c + a2b9d11 commit 78ff567
Show file tree
Hide file tree
Showing 6 changed files with 631 additions and 0 deletions.
85 changes: 85 additions & 0 deletions examples/tiva-c-launchpad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch

use core::fmt::Write;
use cortex_m_rt::entry;
use tm4c123x_hal::eeprom::{
Blocks, Eeprom, EepromAddress, EepromError, Erase, Read, Write as EepromWrite,
};
use tm4c123x_hal::{self as hal, prelude::*};

#[entry]
Expand All @@ -20,6 +23,17 @@ fn main() -> ! {

let mut porta = p.GPIO_PORTA.split(&sc.power_control);

let mut eeprom = Eeprom::new(p.EEPROM, &sc.power_control);

match eeprom_test_all(&mut eeprom) {
Ok(_) => {
// Huzzah!
}
Err(code) => {
panic!("Error detected while testing EEPROM: {}", code);
}
}

// Activate UART
let mut uart = hal::serial::Serial::uart0(
p.UART0,
Expand All @@ -43,3 +57,74 @@ fn main() -> ! {
counter = counter.wrapping_add(1);
}
}

pub fn eeprom_test_write_read(
eeprom: &mut Eeprom,
address: &EepromAddress,
data_to_write: &[u8],
read_buffer: &mut [u8],
) -> Result<(), EepromError> {
eeprom.write(address, &data_to_write)?;
eeprom.read(address, data_to_write.len(), read_buffer)?;

for (i, byte) in data_to_write.iter().enumerate() {
assert_eq!(*byte, read_buffer[i], "Read data differs from written data");
}

Ok(())
}

pub fn eeprom_test_all(eeprom: &mut Eeprom) -> Result<(), EepromError> {
let mut buffer = [0 as u8; 64]; // 64 byte read buffer

// Sanity check for simple mapping from word offset to an EepromAddress
let mut address = eeprom.word_index_to_address(52).unwrap();
assert_eq!(address.block(), 3, "Word 52 should be in block 3, offset 4");
assert_eq!(
address.offset(),
4,
"Word 52 should be in block 3, offset 4"
);

// Sanity check for EepromAddress to word offset
let word_index = eeprom.address_to_word_index(&address).unwrap();
assert_eq!(
word_index, 52,
"Word index for block 3, offset 4 should be 52"
);

// Simplest case, middle of a block, no straddle
let test_array_1: [u8; 4] = [1, 2, 3, 4];
eeprom_test_write_read(eeprom, &mut address, &test_array_1, &mut buffer)?;

// Test boundry conditions for access that straddles a block
let test_array_2: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let address_straddle_block = EepromAddress::new(0, 15);
eeprom_test_write_read(eeprom, &address_straddle_block, &test_array_2, &mut buffer)?;

eeprom.erase(&address_straddle_block, test_array_2.len())?;
eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?;
for i in 0..test_array_2.len() {
assert_eq!(buffer[i], 0, "Buffer should be all 0's")
}

// Test the block erase using the straddle address and data
eeprom.write(&address_straddle_block, &test_array_2)?;
eeprom.erase_block(0)?;
eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?;
for i in 0..test_array_2.len() {
match i {
0..=3 => {
assert_eq!(buffer[i], 0, "Buffer[0..3] should be all 0's");
}
_ => {
assert_eq!(
buffer[i], test_array_2[i],
"Buffer[4..9] should match test_array_2"
)
}
}
}

Ok(())
}
2 changes: 2 additions & 0 deletions tm4c-hal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ depending on your processor.

## Changelog

* Basic EEPROM Read, Write, Erase added

### Unreleased Changes ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/master/tm4c-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c-hal-0.4.1...master))

* Implement use of sealed traits by downstream crates (i.e. `tm4c123x-hal` and `tm4c129x-hal`)
Expand Down
142 changes: 142 additions & 0 deletions tm4c-hal/src/eeprom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Code for the EEProm module.
//!
//! Tested on a TM4C123 Tiva C Series Launchpad
//!
//! Note: This code manually increments the EEBLOCK and EEOFFSET registers
//! after each read and write instead of using the EERDWRINC register. The
//! debugger was giving inconsistent register results for the EEOFFSET register
//! after using EERDWRINC. Also, the EERDWRINC does not increment the block in
//! the case of a wrap of the offset, so it seems less useful for data that
//! spans blocks.
//!
//! This flexibility comes at the cost of efficiency, as the
//! datasheet calls for at least 4 cycles of delay after setting the EEBLOCK
//! register.
/// Possible errors for the Flash memory module
#[derive(Debug, PartialEq)]
pub enum EepromError {
/// Eeprom is not finished
Busy,
/// Address is out of bounds
AddressOutOfBounds,
/// Block is out of bounds
BlockOutOfBounds,
/// Offset is out of bounds
OffsetOutOfBounds,
/// Indicates that writing data would exceed the EEPROM memory space
WriteWouldOverflow,
/// Indicates that reading data would exceed the EEPROM memory space
ReadWouldOverflow,
/// Requesting to read more data than the provided buffer can hold
ReadBufferTooSmall,
}

impl core::fmt::Display for EepromError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
EepromError::Busy => write!(f, "Eeprom is busy"),
EepromError::AddressOutOfBounds => write!(f, "Address is out of bounds"),
EepromError::BlockOutOfBounds => write!(f, "Block is out of bounds"),
EepromError::OffsetOutOfBounds => write!(f, "Offset is out of bounds"),
EepromError::WriteWouldOverflow => {
write!(f, "Writing this data would overflow the EEPROM")
}
EepromError::ReadWouldOverflow => {
write!(f, "Reading this data would overflow the EEPROM")
}
EepromError::ReadBufferTooSmall => write!(f, "Allocated buffer too small for reading"),
}
}
}

/// Struct used to pack the block and offset
#[derive(Clone, Copy)]
pub struct EepromAddress {
/// Eeprom block
block: usize,
/// Eeprom offset in a block
offset: usize,
}

impl EepromAddress {
/// Creates a new EepromAddres with configured block and offset
pub fn new(block: usize, offset: usize) -> Self {
EepromAddress { block, offset }
}

/// Returns the block
pub fn block(&self) -> usize {
self.block
}

/// Returns the offset
pub fn offset(&self) -> usize {
self.offset
}

/// Increments the offset by one, if that would cause an overflow, increment the block. If
/// both the block and offset wrap, the output for the new block and offset
/// will both be 0.
pub fn increment(&mut self, offset_size: usize, block_size: usize) -> &mut Self {
self.offset += 1;
if self.offset >= offset_size {
self.offset = 0;
self.block += 1;
if self.block >= block_size {
self.block = 0;
}
}

self
}
}

/// Series of traits to make access blocks easier
pub trait Blocks {
/// Returns the blocksize for read / write to the flash
fn block_size(&self) -> Result<usize, EepromError>;

/// Returns the EepromAddress for a given index. Valid indexes are 0 to
/// EEPROM_END_ADDRESS_WORDS.
fn word_index_to_address(&self, index: usize) -> Result<EepromAddress, EepromError>;

/// Gives the the word index (0 to EEPROM_END_ADDRESS_WORDS) for a
/// given EepromAddress
fn address_to_word_index(&self, block: &EepromAddress) -> Result<usize, EepromError>;
}

/// Erase functions of the EEPROM
pub trait Erase {
/// Erase (zero out) data starting at an address spanning a length of bytes (not words!)
fn erase(&mut self, address: &EepromAddress, length_bytes: usize) -> Result<(), EepromError>;

/// Erase (zero out) a block
fn erase_block(&mut self, block: usize) -> Result<(), EepromError>;
}

/// Check if the Eeprom is busy
pub trait Busy {
/// Check the EEDONE register, true if busy
fn is_busy(&self) -> bool;

/// Blocks until the EEPROM is not busy
fn wait(&self);
}

/// Write data to the EEPROM
pub trait Write {
/// Write data to a flash address
fn write(&mut self, address: &EepromAddress, data: &[u8]) -> Result<(), EepromError>;
}

/// Read data from the EEPROM
pub trait Read {
/// Eeprom Address to start reading data from
fn read(
&mut self,
address: &EepromAddress,
bytes_to_read: usize,
buffer: &mut [u8],
) -> Result<(), EepromError>;
}
1 change: 1 addition & 0 deletions tm4c-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

pub mod bb;
pub mod delay;
pub mod eeprom;
pub mod gpio;
pub mod i2c;
pub mod serial;
Expand Down
Loading

0 comments on commit 78ff567

Please sign in to comment.