firmware: add I2C

This commit is contained in:
David Lenfesty 2023-03-24 20:54:59 -06:00
parent bf482e740d
commit ac746a8c5a

104
firmware/src/i2c.rs Normal file
View File

@ -0,0 +1,104 @@
//! I2C driver for my amlib i2c interface implementation.
//!
//! See `gateware/i2c.py` for register information
/// TODO repr(C) a register bank, and add instances
// Using the blocking API because the peripheral requires fairly tight timing to operate
// correctly, and I don't feel like writing the gateware to resolve that.
use embedded_hal::blocking::i2c::{Write, Read, SevenBitAddress, Transactional, Operation};
use crate::{read_reg, write_reg};
use core::arch::asm;
// TODO I think there may be bus address semantics I'm not 100% on.
// There's a possiblity these addresses are wrong, and they need to be 4 bytes each
const CR: u32 = 0;
const SR: u32 = 1;
const DWR: u32 = 2;
const DRR: u32 = 3;
#[derive(Clone, Copy, Debug)]
pub enum Error {
/// Device is busy for some reason
Busy,
/// I2C bus returned a NACK
Nack,
}
pub struct AmlibI2c {
base_addr: u32,
}
impl AmlibI2c {
pub fn new(base_addr: u32) -> Self {
AmlibI2c { base_addr }
}
fn wait_while_busy(&self) {
unsafe {
while read_reg::<u32>(self.base_addr + SR) & 1 != 0 {
asm!("nop");
}
}
}
}
impl Write<SevenBitAddress> for AmlibI2c {
// TODO errors
type Error = Error;
fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> {
unsafe {
if (read_reg::<u32>(self.base_addr + SR) & 1) != 0 {
return Err(Error::Busy);
}
// START
write_reg(self.base_addr + CR, 0x01u32);
// Pre-load data w/ address (R/~W = 0)
write_reg(self.base_addr + DWR, (address << 1) as u32);
self.wait_while_busy();
// Send address byte
write_reg(self.base_addr + CR, 0x04u32);
if read_reg::<u32>(self.base_addr + SR) & 0x02 != 0 {
return Err(Error::Nack);
}
for byte in bytes {
// Write byte
write_reg(self.base_addr + DWR, *byte as u32);
self.wait_while_busy();
// Send byte once done sending the last byte
write_reg(self.base_addr + CR, 0x04u32);
}
self.wait_while_busy();
// STOP
write_reg(self.base_addr + CR, 0x02u32);
self.wait_while_busy();
Ok(())
}
}
}
impl Read<SevenBitAddress> for AmlibI2c {
// TODO errors
type Error = Error;
fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> {
Ok(())
}
}
impl Transactional for AmlibI2c {
type Error = Error;
fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>])
-> Result<(), Self::Error> {
Ok(())
}
}