use core::marker::PhantomData;
use crate::pac;
const BASE: usize = 0x0640;
const CTLW0: usize = 0x00; const CTLW1: usize = 0x02; const BRW: usize = 0x06; const STATW: usize = 0x08; const RXBUF: usize = 0x0C; const TXBUF: usize = 0x0E; const I2COA0: usize = 0x14; const I2CSA: usize = 0x20; const IE: usize = 0x2A; const IFG: usize = 0x2C; const IV: usize = 0x2E;
const UCSWRST: u16 = 1 << 0; const UCTXSTT: u16 = 1 << 1; const UCTXSTP: u16 = 1 << 2; const UCTXNACK: u16 = 1 << 3; const UCTR: u16 = 1 << 4; const UCSSEL_SMCLK: u16 = 0b10 << 6; const UCSYNC: u16 = 1 << 8; const UCMODE_I2C: u16 = 0b11 << 9; const UCMST: u16 = 1 << 11;
const UCCLTO_28MS: u16 = 0b01 << 6;
const UCBBUSY: u16 = 1 << 4; const UCGC: u16 = 1 << 5;
const UCRXIFG: u16 = 1 << 0; const UCTXIFG: u16 = 1 << 1; const UCSTTIFG: u16 = 1 << 2; const UCSTPIFG: u16 = 1 << 3; const UCNACKIFG: u16 = 1 << 5; const UCCLTOIFG: u16 = 1 << 7;
const TIMEOUT_BUDGET: u32 = 2_000;
const P1IN: usize = 0x0200;
const P1OUT: usize = 0x0202;
const P1DIR: usize = 0x0204;
const P1REN: usize = 0x0206;
const P1SEL0: usize = 0x020A;
const P1SEL1: usize = 0x020C;
const P1_SDA: u8 = 1 << 6; const P1_SCL: u8 = 1 << 7; const P1_I2C_PINS: u8 = P1_SDA | P1_SCL;
#[inline(always)]
unsafe fn read_reg(addr: usize) -> u16 {
(addr as *const u16).read_volatile()
}
#[inline(always)]
unsafe fn write_reg(addr: usize, val: u16) {
(addr as *mut u16).write_volatile(val);
}
#[inline(always)]
unsafe fn set_bits_u16(addr: usize, mask: u16) {
write_reg(addr, read_reg(addr) | mask);
}
#[inline(always)]
unsafe fn clear_bits_u16(addr: usize, mask: u16) {
write_reg(addr, read_reg(addr) & !mask);
}
#[inline(always)]
unsafe fn set_bits_u8(addr: usize, mask: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() | mask);
}
#[inline(always)]
unsafe fn clear_bits_u8(addr: usize, mask: u8) {
let p = addr as *mut u8;
p.write_volatile(p.read_volatile() & !mask);
}
#[inline(always)]
unsafe fn read_u8(addr: usize) -> u8 {
(addr as *const u8).read_volatile()
}
#[inline(never)]
fn spin(iters: u16) {
for _ in 0..iters {
unsafe {
let _ = read_u8(P1IN);
}
}
}
fn bus_clear() {
unsafe {
clear_bits_u8(P1SEL1, P1_I2C_PINS);
clear_bits_u8(P1SEL0, P1_I2C_PINS);
clear_bits_u8(P1DIR, P1_SDA);
set_bits_u8(P1OUT, P1_SDA);
set_bits_u8(P1REN, P1_SDA);
set_bits_u8(P1OUT, P1_SCL);
set_bits_u8(P1DIR, P1_SCL);
spin(100);
if read_u8(P1IN) & P1_SDA == 0 {
for _ in 0..9 {
clear_bits_u8(P1OUT, P1_SCL); spin(100);
set_bits_u8(P1OUT, P1_SCL); spin(100);
if read_u8(P1IN) & P1_SDA != 0 {
break; }
}
}
clear_bits_u8(P1DIR, P1_I2C_PINS);
clear_bits_u8(P1REN, P1_I2C_PINS);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Error {
AddressNack,
DataNack,
Timeout,
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
match self {
Error::AddressNack => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address),
Error::DataNack => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Data),
Error::Timeout => ErrorKind::Bus,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Config {
pub clock_freq: u32,
pub scl_freq: u32,
}
impl Config {
pub fn new(clock_freq: u32) -> Self {
Config {
clock_freq,
scl_freq: 100_000,
}
}
pub fn scl_freq(mut self, scl_freq: u32) -> Self {
self.scl_freq = scl_freq;
self
}
fn prescaler(&self) -> u16 {
let div = self.clock_freq / self.scl_freq.max(1);
div.clamp(1, u16::MAX as u32) as u16
}
}
pub struct I2c {
_not_send: PhantomData<*const ()>,
}
impl I2c {
fn init(config: Config) -> Self {
let ctlw0 = UCSWRST | UCSYNC | UCMODE_I2C | UCMST | UCSSEL_SMCLK;
bus_clear();
unsafe {
write_reg(BASE + CTLW0, ctlw0);
write_reg(BASE + CTLW1, UCCLTO_28MS);
write_reg(BASE + BRW, config.prescaler());
set_bits_u8(P1SEL1, P1_I2C_PINS);
clear_bits_u8(P1SEL0, P1_I2C_PINS);
write_reg(BASE + CTLW0, ctlw0 & !UCSWRST);
}
I2c {
_not_send: PhantomData,
}
}
pub fn probe(&mut self, address: u8) -> bool {
use embedded_hal::i2c::I2c as _;
self.write(address, &[]).is_ok()
}
fn wait(&self, poll: fn() -> Option<Result<(), Error>>) -> Result<(), Error> {
let mut budget = TIMEOUT_BUDGET;
loop {
if let Some(r) = poll() {
return r;
}
let clock_low_timeout = unsafe { read_reg(BASE + IFG) } & UCCLTOIFG != 0;
budget -= 1;
if clock_low_timeout || budget == 0 {
self.recover();
return Err(Error::Timeout);
}
}
}
fn recover(&self) {
unsafe {
set_bits_u16(BASE + CTLW0, UCSWRST);
clear_bits_u16(BASE + IFG, UCCLTOIFG | UCNACKIFG);
clear_bits_u16(BASE + CTLW0, UCSWRST);
}
}
fn stop(&self) {
unsafe {
set_bits_u16(BASE + CTLW0, UCTXSTP);
}
let _ = self.wait(|| {
if unsafe { read_reg(BASE + CTLW0) } & UCTXSTP == 0 {
Some(Ok(()))
} else {
None
}
});
}
fn wait_tx_ready(&self) -> Result<(), Error> {
let r = self.wait(|| {
let ifg = unsafe { read_reg(BASE + IFG) };
if ifg & UCNACKIFG != 0 {
Some(Err(Error::DataNack))
} else if ifg & UCTXIFG != 0 {
Some(Ok(()))
} else {
None
}
});
if let Err(Error::DataNack) = r {
self.stop();
}
r
}
fn write_run(
&mut self,
ops: &[embedded_hal::i2c::Operation<'_>],
send_stop: bool,
) -> Result<(), Error> {
unsafe {
clear_bits_u16(BASE + IFG, UCNACKIFG | UCCLTOIFG);
set_bits_u16(BASE + CTLW0, UCTR | UCTXSTT);
}
self.wait(|| {
if unsafe { read_reg(BASE + CTLW0) } & UCTXSTT == 0 {
Some(Ok(()))
} else {
None
}
})?;
if unsafe { read_reg(BASE + IFG) } & UCNACKIFG != 0 {
self.stop();
return Err(Error::AddressNack);
}
for op in ops {
if let embedded_hal::i2c::Operation::Write(bytes) = op {
for &b in *bytes {
self.wait_tx_ready()?;
unsafe { write_reg(BASE + TXBUF, b as u16) };
}
}
}
self.wait_tx_ready()?;
if send_stop {
self.stop();
}
Ok(())
}
fn read_run(
&mut self,
ops: &mut [embedded_hal::i2c::Operation<'_>],
send_stop: bool,
) -> Result<(), Error> {
let total: usize = ops
.iter()
.map(|o| match o {
embedded_hal::i2c::Operation::Read(buf) => buf.len(),
_ => 0,
})
.sum();
unsafe {
clear_bits_u16(BASE + IFG, UCNACKIFG | UCCLTOIFG);
clear_bits_u16(BASE + CTLW0, UCTR);
set_bits_u16(BASE + CTLW0, UCTXSTT);
}
self.wait(|| {
if unsafe { read_reg(BASE + CTLW0) } & UCTXSTT == 0 {
Some(Ok(()))
} else {
None
}
})?;
if unsafe { read_reg(BASE + IFG) } & UCNACKIFG != 0 {
self.stop();
return Err(Error::AddressNack);
}
if send_stop && total == 1 {
unsafe { set_bits_u16(BASE + CTLW0, UCTXSTP) };
}
let mut idx = 0usize;
for op in ops {
if let embedded_hal::i2c::Operation::Read(buf) = op {
for slot in buf.iter_mut() {
if send_stop && total > 1 && idx == total - 1 {
unsafe { set_bits_u16(BASE + CTLW0, UCTXSTP) };
}
self.wait(|| {
if unsafe { read_reg(BASE + IFG) } & UCRXIFG != 0 {
Some(Ok(()))
} else {
None
}
})?;
*slot = unsafe { read_reg(BASE + RXBUF) } as u8;
idx += 1;
}
}
}
if send_stop {
if total == 0 {
unsafe { set_bits_u16(BASE + CTLW0, UCTXSTP) };
}
self.wait(|| {
if unsafe { read_reg(BASE + CTLW0) } & UCTXSTP == 0 {
Some(Ok(()))
} else {
None
}
})?;
}
Ok(())
}
}
pub trait I2cExt {
fn into_i2c(self, config: Config) -> I2c;
}
impl I2cExt for pac::UsciB0I2cMode {
fn into_i2c(self, config: Config) -> I2c {
I2c::init(config)
}
}
pub use crate::i2c_slave::{decode_iv, encode_own_address, AddressError, SlaveIv};
pub use crate::i2c_slave::{
IV_ARBITRATION_LOST, IV_BYTE_COUNTER, IV_CLOCK_LOW_TIMEOUT, IV_NACK, IV_NINTH_BIT, IV_NONE,
IV_RX, IV_START, IV_STOP, IV_TX,
};
#[derive(Clone, Copy, Debug)]
pub struct SlaveConfig {
pub address: u8,
pub general_call: bool,
}
impl SlaveConfig {
pub fn new(address: u8) -> Self {
SlaveConfig {
address,
general_call: false,
}
}
pub fn general_call(mut self) -> Self {
self.general_call = true;
self
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SlaveEvent {
Start { read: bool },
Received(u8),
TxRequest,
Stop,
}
pub struct I2cSlave {
transmitting: bool,
_not_send: PhantomData<*const ()>,
}
impl I2cSlave {
fn init(config: SlaveConfig) -> Result<Self, AddressError> {
let oa0 = encode_own_address(config.address, config.general_call)?;
let ctlw0 = UCSWRST | UCSYNC | UCMODE_I2C;
unsafe {
write_reg(BASE + CTLW0, ctlw0);
write_reg(BASE + CTLW1, 0);
write_reg(BASE + I2COA0, oa0);
set_bits_u8(P1SEL1, P1_I2C_PINS);
clear_bits_u8(P1SEL0, P1_I2C_PINS);
write_reg(BASE + CTLW0, ctlw0 & !UCSWRST);
}
Ok(I2cSlave {
transmitting: false,
_not_send: PhantomData,
})
}
pub fn poll(&mut self) -> Option<SlaveEvent> {
let ifg = unsafe { read_reg(BASE + IFG) };
if ifg & UCRXIFG != 0 {
return Some(SlaveEvent::Received(unsafe { read_reg(BASE + RXBUF) } as u8));
}
if ifg & UCSTPIFG != 0 {
unsafe { clear_bits_u16(BASE + IFG, UCSTPIFG) };
let was_transmitting = self.transmitting;
self.transmitting = false;
if was_transmitting {
self.flush_stale_tx_byte();
}
return Some(SlaveEvent::Stop);
}
if ifg & UCSTTIFG != 0 {
unsafe { clear_bits_u16(BASE + IFG, UCSTTIFG) };
let read = unsafe { read_reg(BASE + CTLW0) } & UCTR != 0;
self.transmitting = read;
return Some(SlaveEvent::Start { read });
}
if self.transmitting && ifg & UCTXIFG != 0 {
return Some(SlaveEvent::TxRequest);
}
None
}
pub fn wait_event(&mut self) -> SlaveEvent {
loop {
if let Some(e) = self.poll() {
return e;
}
}
}
pub fn write_byte(&mut self, byte: u8) {
unsafe { write_reg(BASE + TXBUF, byte as u16) };
}
pub fn nack_next_byte(&mut self) {
unsafe { set_bits_u16(BASE + CTLW0, UCTXNACK) };
}
pub fn addressed_as_general_call(&self) -> bool {
let statw = unsafe { read_reg(BASE + STATW) };
statw & UCGC != 0
}
pub fn bus_busy(&self) -> bool {
let statw = unsafe { read_reg(BASE + STATW) };
statw & UCBBUSY != 0
}
fn flush_stale_tx_byte(&mut self) {
let ifg = unsafe { read_reg(BASE + IFG) };
if ifg & UCTXIFG != 0 {
return; }
unsafe {
let ie = read_reg(BASE + IE);
set_bits_u16(BASE + CTLW0, UCSWRST);
clear_bits_u16(BASE + CTLW0, UCSWRST);
write_reg(BASE + IE, ie);
}
}
pub fn free(self) -> pac::UsciB0I2cMode {
unsafe {
set_bits_u16(BASE + CTLW0, UCSWRST);
write_reg(BASE + IE, 0);
write_reg(BASE + I2COA0, 0);
pac::Peripherals::steal().usci_b0_i2c_mode
}
}
}
#[cfg(feature = "critical-section")]
#[derive(Clone, Copy, Default, Debug)]
pub struct SlaveInterrupts {
pub start: bool,
pub stop: bool,
pub rx: bool,
pub tx: bool,
}
#[cfg(feature = "critical-section")]
impl I2cSlave {
pub fn enable_interrupts(&mut self, which: SlaveInterrupts) {
let mut mask = 0u16;
if which.start {
mask |= UCSTTIFG;
}
if which.stop {
mask |= UCSTPIFG;
}
if which.rx {
mask |= UCRXIFG;
}
if which.tx {
mask |= UCTXIFG;
}
critical_section::with(|_| unsafe {
set_bits_u16(BASE + IE, mask);
});
}
pub fn disable_interrupts(&mut self) {
critical_section::with(|_| unsafe {
write_reg(BASE + IE, 0);
});
}
}
pub fn read_iv() -> u16 {
unsafe { read_reg(BASE + IV) }
}
pub fn isr_read_byte() -> u8 {
unsafe { read_reg(BASE + RXBUF) as u8 }
}
pub fn isr_write_byte(byte: u8) {
unsafe { write_reg(BASE + TXBUF, byte as u16) };
}
pub fn isr_disable_interrupts() {
unsafe { write_reg(BASE + IE, 0) };
}
pub trait I2cSlaveExt {
fn into_i2c_slave(self, config: SlaveConfig) -> Result<I2cSlave, AddressError>;
}
impl I2cSlaveExt for pac::UsciB0I2cMode {
fn into_i2c_slave(self, config: SlaveConfig) -> Result<I2cSlave, AddressError> {
I2cSlave::init(config)
}
}
impl embedded_hal::i2c::ErrorType for I2c {
type Error = Error;
}
impl embedded_hal::i2c::I2c<embedded_hal::i2c::SevenBitAddress> for I2c {
fn transaction(
&mut self,
address: embedded_hal::i2c::SevenBitAddress,
operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
if operations.is_empty() {
return Ok(());
}
unsafe { write_reg(BASE + I2CSA, address as u16) };
self.wait(|| {
if unsafe { read_reg(BASE + CTLW0) } & UCTXSTP == 0 {
Some(Ok(()))
} else {
None
}
})?;
let n = operations.len();
let mut i = 0;
while i < n {
let is_read = matches!(operations[i], embedded_hal::i2c::Operation::Read(_));
let mut j = i + 1;
while j < n
&& matches!(operations[j], embedded_hal::i2c::Operation::Read(_)) == is_read
{
j += 1;
}
let is_last_run = j == n;
if is_read {
self.read_run(&mut operations[i..j], is_last_run)?;
} else {
self.write_run(&operations[i..j], is_last_run)?;
}
i = j;
}
Ok(())
}
}