firmware: add I2C
This commit is contained in:
parent
bf482e740d
commit
ac746a8c5a
104
firmware/src/i2c.rs
Normal file
104
firmware/src/i2c.rs
Normal 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(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user