use clock;
use hal::blocking::i2c::{Read, Write, WriteRead};
use sercom::pads::*;
use target_device::sercom0::I2CM;
use target_device::{PM, SERCOM0, SERCOM1, SERCOM2, SERCOM3};
#[cfg(feature = "samd21g18a")]
use target_device::{SERCOM4, SERCOM5};
use time::Hertz;
const BUS_STATE_IDLE: u8 = 1;
const BUS_STATE_OWNED: u8 = 2;
const MASTER_ACT_READ: u8 = 2;
const MASTER_ACT_STOP: u8 = 3;
macro_rules! i2c {
([
$($Type:ident: ($pad0:ident, $pad1:ident, $SERCOM:ident, $powermask:ident, $clock:ident),)+
]) => {
$(
pub struct $Type {
sda: $pad0,
scl: $pad1,
sercom: $SERCOM,
}
impl $Type {
pub fn new<F: Into<Hertz>>(
clock: &clock::$clock,
freq: F,
sercom: $SERCOM,
pm: &mut PM,
sda: $pad0,
scl: $pad1,
) -> Self {
pm.apbcmask.modify(|_, w| w.$powermask().set_bit());
unsafe {
sercom.i2cm().ctrla.modify(|_, w| w.swrst().set_bit());
while sercom.i2cm().syncbusy.read().swrst().bit_is_set()
|| sercom.i2cm().ctrla.read().swrst().bit_is_set()
{}
sercom.i2cm().ctrla.modify(|_, w| w.mode().i2c_master());
while sercom.i2cm().syncbusy.read().enable().bit_is_set() {}
let gclk = clock.freq();
let baud = (gclk.0 / (2 * freq.into().0) - 1) as u8;
sercom.i2cm().baud.modify(|_, w| w.baud().bits(baud));
sercom.i2cm().ctrla.modify(|_, w| w.enable().set_bit());
while sercom.i2cm().syncbusy.read().enable().bit_is_set() {}
sercom
.i2cm()
.status
.modify(|_, w| w.busstate().bits(BUS_STATE_IDLE));
while sercom.i2cm().syncbusy.read().sysop().bit_is_set() {}
}
Self { sda, scl, sercom }
}
pub fn free(self) -> ($pad0, $pad1, $SERCOM) {
(self.sda, self.scl, self.sercom)
}
fn start_tx_write(&mut self, addr: u8) -> Result<(), I2CError> {
loop {
match self.i2cm().status.read().busstate().bits() {
BUS_STATE_IDLE | BUS_STATE_OWNED => break,
_ => continue,
}
}
unsafe {
self.i2cm()
.addr
.write(|w| w.addr().bits((addr as u16) << 1));
}
while !self.i2cm().intflag.read().mb().bit_is_set() {}
self.status_to_err()
}
fn status_to_err(&mut self) -> Result<(), I2CError> {
let status = self.i2cm().status.read();
if status.arblost().bit_is_set() {
return Err(I2CError::ArbitrationLost);
}
if status.buserr().bit_is_set() {
return Err(I2CError::BusError);
}
if status.rxnack().bit_is_set() {
return Err(I2CError::Nack);
}
if status.lowtout().bit_is_set() || status.sexttout().bit_is_set()
|| status.mexttout().bit_is_set()
{
return Err(I2CError::Timeout);
}
Ok(())
}
fn start_tx_read(&mut self, addr: u8) -> Result<(), I2CError> {
loop {
match self.i2cm().status.read().busstate().bits() {
BUS_STATE_IDLE | BUS_STATE_OWNED => break,
_ => continue,
}
}
self.i2cm().intflag.modify(|_, w| w.error().clear_bit());
unsafe {
self.i2cm()
.addr
.write(|w| w.addr().bits(((addr as u16) << 1) | 1));
}
loop {
let intflag = self.i2cm().intflag.read();
if intflag.mb().bit_is_set() {
return Err(I2CError::ArbitrationLost);
}
if intflag.sb().bit_is_set() || intflag.error().bit_is_set() {
break;
}
}
self.status_to_err()
}
fn wait_sync(&mut self) {
while self.i2cm().syncbusy.read().sysop().bit_is_set() {}
}
fn cmd(&mut self, cmd: u8) {
unsafe {
self.i2cm().ctrlb.modify(|_, w| w.cmd().bits(cmd));
}
self.wait_sync();
}
fn cmd_stop(&mut self) {
self.cmd(MASTER_ACT_STOP)
}
fn cmd_read(&mut self) {
unsafe {
self.i2cm().ctrlb.modify(|_, w| {
w.ackact().clear_bit();
w.cmd().bits(MASTER_ACT_READ)
});
}
self.wait_sync();
}
fn i2cm(&mut self) -> &I2CM {
&self.sercom.i2cm()
}
fn send_bytes(&mut self, bytes: &[u8]) -> Result<(), I2CError> {
for b in bytes {
unsafe {
self.i2cm().data.write(|w| w.bits(*b));
}
loop {
let intflag = self.i2cm().intflag.read();
if intflag.mb().bit_is_set() || intflag.error().bit_is_set() {
break;
}
}
self.status_to_err()?;
}
Ok(())
}
fn read_one(&mut self) -> u8 {
while !self.i2cm().intflag.read().sb().bit_is_set() {}
self.i2cm().data.read().bits()
}
fn fill_buffer(&mut self, buffer: &mut [u8]) -> Result<(), I2CError> {
let mut iter = buffer.iter_mut();
*iter.next().expect("buffer len is at least 1") = self.read_one();
loop {
match iter.next() {
None => break,
Some(dest) => {
self.cmd_read();
*dest = self.read_one();
}
}
}
self.i2cm().ctrlb.modify(|_, w| w.ackact().set_bit());
Ok(())
}
fn do_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), I2CError> {
self.start_tx_write(addr)?;
self.send_bytes(bytes)
}
fn do_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), I2CError> {
self.start_tx_read(addr)?;
self.fill_buffer(buffer)
}
fn do_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), I2CError> {
self.start_tx_write(addr)?;
self.send_bytes(bytes)?;
self.start_tx_read(addr)?;
self.fill_buffer(buffer)
}
}
impl Write for $Type {
type Error = I2CError;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
let res = self.do_write(addr, bytes);
self.cmd_stop();
res
}
}
impl Read for $Type {
type Error = I2CError;
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
let res = self.do_read(addr, buffer);
self.cmd_stop();
res
}
}
impl WriteRead for $Type {
type Error = I2CError;
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
let res = self.do_write_read(addr, bytes, buffer);
self.cmd_stop();
res
}
}
)+
};
}
i2c!([
I2CMaster0:
(
Sercom0Pad0,
Sercom0Pad1,
SERCOM0,
sercom0_,
Sercom0CoreClock
),
I2CMaster1:
(
Sercom1Pad0,
Sercom1Pad1,
SERCOM1,
sercom1_,
Sercom1CoreClock
),
I2CMaster2:
(
Sercom2Pad0,
Sercom2Pad1,
SERCOM2,
sercom2_,
Sercom2CoreClock
),
I2CMaster3:
(
Sercom3Pad0,
Sercom3Pad1,
SERCOM3,
sercom3_,
Sercom3CoreClock
),
]);
#[cfg(feature = "samd21g18a")]
i2c!([
I2CMaster4:
(
Sercom4Pad0,
Sercom4Pad1,
SERCOM4,
sercom4_,
Sercom4CoreClock
),
I2CMaster5:
(
Sercom5Pad0,
Sercom5Pad1,
SERCOM5,
sercom5_,
Sercom5CoreClock
),
]);
#[derive(Debug)]
pub enum I2CError {
ArbitrationLost,
AddressError,
BusError,
Timeout,
Nack,
}