Slave: write Starts working work in progress
This commit is contained in:
parent
9424379268
commit
007855423e
@ -9,16 +9,23 @@ pub use _version::*;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
Bus,
|
||||
Arbitration,
|
||||
Arbitration, // in case of master mode: lost arbitration to another master
|
||||
Nack,
|
||||
Timeout,
|
||||
Crc,
|
||||
Overrun,
|
||||
ZeroLengthTransfer,
|
||||
Collission, // in case of slave mode, during sending data to master
|
||||
BufferEmpty,
|
||||
BufferFull,
|
||||
BufferNotEmpty,
|
||||
BufferNotFilled,
|
||||
BufferSize,
|
||||
OkBufferTransferred, // not really an error, but signalling that the slave does nack the last byte
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::cell::RefCell;
|
||||
use core::cmp;
|
||||
#[cfg(feature = "time")]
|
||||
use core::future::poll_fn;
|
||||
@ -9,9 +10,12 @@ use embassy_embedded_hal::SetConfig;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
use stm32_metapac::i2c::vals;
|
||||
|
||||
use crate::dma::NoDma;
|
||||
#[cfg(feature = "time")]
|
||||
@ -34,11 +38,101 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
let regs = T::regs();
|
||||
let isr = regs.isr().read();
|
||||
|
||||
if isr.tcr() || isr.tc() {
|
||||
T::state().waker.wake();
|
||||
}
|
||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||
// the interrupt
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
if state_m.slave_mode {
|
||||
// ============================================ slave interrupt state_m machine
|
||||
if isr.berr() {
|
||||
regs.icr().modify(|w| {
|
||||
w.set_berrcf(true);
|
||||
});
|
||||
state_m.errors += 1;
|
||||
state_m.result = Err(Error::Bus);
|
||||
} else if isr.arlo() {
|
||||
regs.icr().write(|w| w.set_arlocf(true));
|
||||
state_m.result = Err(Error::Arbitration);
|
||||
} else if isr.nackf() {
|
||||
regs.icr().write(|w| w.set_nackcf(true));
|
||||
// Make one extra loop to wait on the stop condition
|
||||
} else if isr.txis() {
|
||||
// send the next byte to the master, or NACK in case of error, then end transaction
|
||||
match state_m.read_byte() {
|
||||
Ok(b) => regs.txdr().write(|w| w.set_txdata(b)),
|
||||
Err(Error::OkBufferTransferred) => {
|
||||
state_m.result = Ok(());
|
||||
// Send a NACK, set nbytes to clear tcr flag
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_nack(true);
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
state_m.result = Err(e);
|
||||
state_m.errors += 1;
|
||||
// Send a NACK, set nbytes to clear tcr flag
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_nack(true);
|
||||
})
|
||||
}
|
||||
};
|
||||
} else if isr.rxne() {
|
||||
let b = regs.rxdr().read().rxdata();
|
||||
// byte is received from master. Store in buffer. In case of error send NACK, then end transaction
|
||||
match state_m.write_byte(b) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
state_m.result = Err(e);
|
||||
state_m.errors += 1;
|
||||
// Send a NACK, set nbytes to clear tcr flag
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_nack(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if isr.stopf() {
|
||||
// Clear the stop condition flag
|
||||
regs.icr().write(|w| w.set_stopcf(true));
|
||||
state_m.ready = true;
|
||||
T::state().waker.wake();
|
||||
} else if isr.tcr() {
|
||||
// This condition Will only happen when reload == 1 and sbr == 1 (slave) and nbytes was written.
|
||||
// Send a NACK, set nbytes to clear tcr flag
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_nack(true);
|
||||
});
|
||||
// Make one extra loop here to wait on the stop condition
|
||||
} else if isr.addr() {
|
||||
// handle the slave is addressed case, first step in the transaction
|
||||
state_m.current_address = isr.addcode();
|
||||
state_m.dir = isr.dir();
|
||||
if state_m.dir == vals::Dir::READ {
|
||||
// Set the nbytes START and prepare to receive bytes into `buffer`.
|
||||
regs.cr2().modify(|w| {
|
||||
// Set number of bytes to transfer: maximum as all incoming bytes will be ACK'ed
|
||||
w.set_nbytes(state_m.get_size());
|
||||
// during sending nbytes automatically send a ACK, stretch clock after last byte
|
||||
w.set_reload(vals::Reload::COMPLETED);
|
||||
});
|
||||
} else {
|
||||
// Set the nbytes to the maximum buffer size and wait for the bytes from the master
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_nbytes(BUFFER_SIZE as u8);
|
||||
w.set_reload(vals::Reload::COMPLETED)
|
||||
});
|
||||
// flush i2c tx register
|
||||
regs.isr().write(|w| w.set_txe(true));
|
||||
}
|
||||
// end address phase, release clock stretching
|
||||
regs.icr().write(|w| w.set_addrcf(true));
|
||||
}
|
||||
// ============================================ end slave interrupt state machine
|
||||
} else {
|
||||
if isr.tcr() || isr.tc() {
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
}); // end of mutex
|
||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||
// the interrupt
|
||||
critical_section::with(|_| {
|
||||
regs.cr1().modify(|w| w.set_tcie(false));
|
||||
});
|
||||
@ -50,15 +144,43 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
pub struct Config {
|
||||
pub sda_pullup: bool,
|
||||
pub scl_pullup: bool,
|
||||
pub slave_address_1: u16,
|
||||
pub slave_address_2: u8,
|
||||
pub slave_address_mask: vals::Oamsk,
|
||||
pub address_11bits: bool,
|
||||
#[cfg(feature = "time")]
|
||||
pub transaction_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Slave address 1 as 7 bit address, in range 0 .. 127
|
||||
pub fn slave_address_7bits(&mut self, address: u8) {
|
||||
// assert!(address < (2 ^ 7));
|
||||
self.slave_address_1 = address as u16;
|
||||
self.address_11bits = false;
|
||||
}
|
||||
/// Slave address 1 as 11 bit address in range 0 .. 2047
|
||||
pub fn slave_address_11bits(&mut self, address: u16) {
|
||||
// assert!(address < (2 ^ 11));
|
||||
self.slave_address_1 = address;
|
||||
self.address_11bits = true;
|
||||
}
|
||||
/// Slave address 2 as 7 bit address in range 0 .. 127.
|
||||
/// The mask makes all slaves within the mask addressable
|
||||
pub fn slave_address_2(&mut self, address: u8, mask: vals::Oamsk) {
|
||||
// assert!(address < (2 ^ 7));
|
||||
self.slave_address_2 = address;
|
||||
self.slave_address_mask = mask;
|
||||
}
|
||||
}
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sda_pullup: false,
|
||||
scl_pullup: false,
|
||||
slave_address_1: 0,
|
||||
slave_address_2: 0,
|
||||
slave_address_mask: vals::Oamsk::NOMASK,
|
||||
address_11bits: false,
|
||||
#[cfg(feature = "time")]
|
||||
transaction_timeout: Duration::from_millis(100),
|
||||
}
|
||||
@ -67,15 +189,150 @@ impl Default for Config {
|
||||
|
||||
pub struct State {
|
||||
waker: AtomicWaker,
|
||||
mutex: Mutex<CriticalSectionRawMutex, RefCell<I2cStateMachine>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
mutex: Mutex::new(RefCell::new(I2cStateMachine::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
struct I2cStateMachine {
|
||||
buffers: [[I2cBuffer; 2]; 2],
|
||||
result: Result<(), Error>,
|
||||
slave_mode: bool,
|
||||
dir: vals::Dir,
|
||||
address1: u16,
|
||||
current_address: u8,
|
||||
errors: u32,
|
||||
ready: bool,
|
||||
}
|
||||
impl I2cStateMachine {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
// first dimension: address type, main or generic, second dimension read or write
|
||||
buffers: [
|
||||
[I2cBuffer::new(), I2cBuffer::new()],
|
||||
[I2cBuffer::new(), I2cBuffer::new()],
|
||||
],
|
||||
result: Ok(()),
|
||||
slave_mode: false,
|
||||
dir: vals::Dir::READ,
|
||||
address1: 0,
|
||||
current_address: 0,
|
||||
errors: 0,
|
||||
ready: false,
|
||||
}
|
||||
}
|
||||
fn read_byte(&mut self) -> Result<u8, Error> {
|
||||
let adress_type = if self.address1 == self.current_address as u16 {
|
||||
AddressType::MainAddress
|
||||
} else {
|
||||
AddressType::GenericAddress
|
||||
};
|
||||
self.buffers[adress_type as usize][vals::Dir::READ as usize as usize].master_read()
|
||||
}
|
||||
fn write_byte(&mut self, b: u8) -> Result<(), Error> {
|
||||
let adress_type = if self.address1 == self.current_address as u16 {
|
||||
AddressType::MainAddress
|
||||
} else {
|
||||
AddressType::GenericAddress
|
||||
};
|
||||
self.buffers[adress_type as usize][vals::Dir::WRITE as usize].master_write(b)
|
||||
}
|
||||
fn get_size(&self) -> u8 {
|
||||
let adress_type = if self.address1 == self.current_address as u16 {
|
||||
AddressType::MainAddress
|
||||
} else {
|
||||
AddressType::GenericAddress
|
||||
};
|
||||
self.buffers[adress_type as usize][self.dir as usize].size
|
||||
}
|
||||
}
|
||||
const BUFFER_SIZE: usize = 64;
|
||||
|
||||
#[repr(usize)]
|
||||
pub enum AddressType {
|
||||
MainAddress = 0,
|
||||
GenericAddress,
|
||||
}
|
||||
|
||||
struct I2cBuffer {
|
||||
buffer: [u8; BUFFER_SIZE],
|
||||
index: usize,
|
||||
size: u8, // only used for the master read slave write scenario
|
||||
}
|
||||
|
||||
impl I2cBuffer {
|
||||
const fn new() -> Self {
|
||||
I2cBuffer {
|
||||
buffer: [0; BUFFER_SIZE],
|
||||
index: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.index = 0;
|
||||
self.size = 0;
|
||||
}
|
||||
/// master read slave write scenario. Master can read until self.size bytes
|
||||
/// If no data available (self.size == 0)
|
||||
fn master_read(&mut self) -> Result<u8, Error> {
|
||||
if self.size == 0 {
|
||||
return Err(Error::BufferEmpty);
|
||||
};
|
||||
if self.index < self.size as usize {
|
||||
let b = self.buffer[self.index];
|
||||
self.index += 1;
|
||||
Ok(b)
|
||||
} else {
|
||||
self.size = 0; // mark buffer empty
|
||||
Err(Error::OkBufferTransferred) // not really an error, but to signal slave should send NACK
|
||||
}
|
||||
}
|
||||
/// master write slave read scenario. Master can write until buffer full
|
||||
fn master_write(&mut self, b: u8) -> Result<(), Error> {
|
||||
if self.index < BUFFER_SIZE {
|
||||
self.buffer[self.index] = b;
|
||||
self.index += 1;
|
||||
self.size = self.index as u8;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::BufferFull)
|
||||
}
|
||||
}
|
||||
// read data into this buffer (master read, slave write)
|
||||
fn from_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let len = buffer.len();
|
||||
if len > self.buffer.len() {
|
||||
return Err(Error::BufferSize);
|
||||
};
|
||||
for i in 0..len {
|
||||
self.buffer[i] = buffer[i];
|
||||
}
|
||||
self.size = len as u8;
|
||||
self.index = 0;
|
||||
Ok(())
|
||||
}
|
||||
// read data from this buffer, and leave empty at the end (master write, slave read)
|
||||
fn to_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
if self.size == 0 {
|
||||
return Err(Error::BufferNotFilled);
|
||||
}
|
||||
if buffer.len() < self.size as usize {
|
||||
return Err(Error::BufferSize);
|
||||
}
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = self.buffer[i];
|
||||
}
|
||||
self.size = 0;
|
||||
self.index = 0;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
@ -136,7 +393,39 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_pe(true);
|
||||
reg.set_nostretch(false);
|
||||
reg.set_sbc(true);
|
||||
});
|
||||
if config.slave_address_1 > 0 {
|
||||
T::regs().oar1().write(|reg| {
|
||||
reg.set_oa1en(false);
|
||||
});
|
||||
let mode = if config.address_11bits {
|
||||
vals::Addmode::BIT10
|
||||
} else {
|
||||
vals::Addmode::BIT7
|
||||
};
|
||||
T::regs().oar1().write(|reg| {
|
||||
reg.set_oa1(config.slave_address_1);
|
||||
reg.set_oa1mode(mode);
|
||||
reg.set_oa1en(true);
|
||||
});
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
state_m.address1 = config.slave_address_1;
|
||||
});
|
||||
}
|
||||
|
||||
if config.slave_address_2 > 0 {
|
||||
T::regs().oar2().write(|reg| {
|
||||
reg.set_oa2en(false);
|
||||
});
|
||||
T::regs().oar2().write(|reg| {
|
||||
reg.set_oa2msk(config.slave_address_mask);
|
||||
reg.set_oa2(config.slave_address_2);
|
||||
reg.set_oa2en(true);
|
||||
});
|
||||
}
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
@ -746,7 +1035,95 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// =========================
|
||||
// async Slave implementation
|
||||
/// Starts listening for slave transactions
|
||||
pub fn slave_start_listen(&mut self) -> Result<(), super::Error> {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_addrie(true);
|
||||
reg.set_txie(true);
|
||||
reg.set_addrie(true);
|
||||
reg.set_rxie(true);
|
||||
reg.set_nackie(true);
|
||||
reg.set_stopie(true);
|
||||
reg.set_errie(true);
|
||||
reg.set_tcie(true);
|
||||
});
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
for i in 0..1 {
|
||||
for j in 0..1 {
|
||||
state_m.buffers[i][j].reset();
|
||||
}
|
||||
}
|
||||
state_m.slave_mode = true;
|
||||
state_m.ready = false;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
// slave stop listening for slave transactions and switch back to master role
|
||||
pub fn slave_stop_listen(&mut self) -> Result<(), super::Error> {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_addrie(false);
|
||||
reg.set_txie(false);
|
||||
reg.set_addrie(false);
|
||||
reg.set_rxie(false);
|
||||
reg.set_nackie(false);
|
||||
reg.set_stopie(false);
|
||||
reg.set_errie(false);
|
||||
reg.set_tcie(false);
|
||||
});
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
state_m.slave_mode = false;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
/// Prepare write data to master (master_read_slave_write) before transaction starts
|
||||
/// Only possible if the buffer is empty, other wise error BufferNotEmpty error
|
||||
pub fn slave_write_buffer(&mut self, buffer: &[u8], address_type: AddressType) -> Result<(), super::Error> {
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
let buf = &mut state_m.buffers[address_type as usize][vals::Dir::READ as usize];
|
||||
if buf.size > 0 {
|
||||
return Err(Error::BufferNotEmpty);
|
||||
};
|
||||
buf.from_buffer(buffer)
|
||||
})
|
||||
}
|
||||
/// Read data from master (master_write_slave_read) after transaction is finished
|
||||
/// Only possible if the buffer is not empty, other wise error BufferNotFilled error
|
||||
pub fn slave_read_buffer(&mut self, buffer: &mut [u8], address_type: AddressType) -> Result<(), super::Error> {
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
let buf = &mut state_m.buffers[address_type as usize][vals::Dir::WRITE as usize];
|
||||
if buf.size == 0 {
|
||||
return Err(Error::BufferEmpty);
|
||||
};
|
||||
buf.to_buffer(buffer)
|
||||
})
|
||||
}
|
||||
/// wait until a slave transaction is finished, and return tuple address, direction and data size
|
||||
pub async fn slave_transaction(&mut self) -> Result<(u8, vals::Dir, u8), Error> {
|
||||
// async wait until addressed
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
T::state().mutex.lock(|f| {
|
||||
let mut state_m = f.borrow_mut();
|
||||
if state_m.ready {
|
||||
state_m.ready = false;
|
||||
// if the dir bit is set it is a master write slave read operation
|
||||
match state_m.result {
|
||||
Ok(()) => return Poll::Ready(Ok((state_m.current_address, state_m.dir, state_m.get_size()))),
|
||||
Err(e) => return Poll::Ready(Err(e)),
|
||||
}
|
||||
} else {
|
||||
return Poll::Pending;
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
// =========================
|
||||
// Blocking public API
|
||||
|
||||
|
@ -75,60 +75,89 @@ async fn main(_spawner: Spawner) {
|
||||
counter += 1;
|
||||
writeln!(&mut writer, "Loop counter: {:?}", counter).unwrap();
|
||||
|
||||
let mut buf_long: [u8; 50] = [0; 50];
|
||||
let mut buf_short: [u8; 20] = [0; 20];
|
||||
let mut buf_rcv: [u8; 20] = [0; 20];
|
||||
let mut buf_10: [u8; 10] = [0; 10];
|
||||
let mut buf_io = [0_u8; 1];
|
||||
let mut buf_20 = [0_u8; 20];
|
||||
let mut buf_64 = [0_u8; 64];
|
||||
let mut buf_65 = [0_u8; 65];
|
||||
|
||||
writeln!(&mut writer, "Start of test").unwrap();
|
||||
|
||||
for i in 0..buf_short.len() {
|
||||
buf_short[i] = 0x41 + (i as u8)
|
||||
for i in 0..buf_20.len() {
|
||||
buf_20[i] = 0x20 + (i as u8)
|
||||
}
|
||||
for i in 0..buf_64.len() {
|
||||
buf_64[i] = 0x40 + (i as u8)
|
||||
}
|
||||
for i in 0..buf_65.len() {
|
||||
buf_65[i] = 0x60 + (i as u8)
|
||||
}
|
||||
|
||||
// test 1: slave address 0x61 should not be addressable
|
||||
match i2c.blocking_write(0x61, &buf_short) {
|
||||
match i2c.blocking_write(0x61, &buf_20) {
|
||||
Ok(_) => writeln!(&mut writer, "Test 1 Error: would expect nack").unwrap(),
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 1 OK: expected NACK error: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x41 good case master write slave read: master does send 20 bytes slave receives 20 bytes
|
||||
match i2c.blocking_write(0x41, &buf_short) {
|
||||
match i2c.blocking_write(0x41, &buf_20) {
|
||||
Ok(_) => writeln!(&mut writer, "Test 0x41 Ok").unwrap(),
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x41 Error: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x42 bad case master write slave read: master does send less than 20 bytes
|
||||
for i in 0..buf_10.len() {
|
||||
buf_10[i] = 0x20 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_write(0x42, &buf_10) {
|
||||
Ok(_) => writeln!(
|
||||
&mut writer,
|
||||
"Test 0x42 Ok. (Master cannot detect that frame is too short) "
|
||||
)
|
||||
.unwrap(),
|
||||
// 0x42 edge case master write exact 64 bytes: must succeed on master and slave
|
||||
match i2c.blocking_write(0x42, &buf_64) {
|
||||
Ok(_) => writeln!(&mut writer, "Test 0x42 Ok. Master write exact 64 bytes").unwrap(),
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x42 error IncorrectFramesize: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x43 bad case master write slave read: master does send more than 20 bytes, slave does NACK
|
||||
for i in 0..buf_long.len() {
|
||||
buf_long[i] = 0x61 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_write(0x43, &buf_long) {
|
||||
Ok(_) => writeln!(&mut writer, "Test 0x43 not ok expected error IncorrectFramesize: ").unwrap(),
|
||||
// 0x43 edge case master write exact 65 bytes: 1 too manyu must fail on master and slave
|
||||
match i2c.blocking_write(0x43, &buf_64) {
|
||||
Ok(_) => writeln!(&mut writer, "Test 0x42 Failed. Expected a Nack").unwrap(),
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x43 Ok Expected IncorrectFrameSize: {:?}", err).unwrap(),
|
||||
Err(err) => writeln!(
|
||||
&mut writer,
|
||||
"Test 0x43 Ok: Got error NACK du to buffer of 1 too big {:?}",
|
||||
err
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
// 0x44 master write_read good case: master sends and expects 20 bytes
|
||||
for i in 0..buf_short.len() {
|
||||
buf_short[i] = 0x41 + (i as u8)
|
||||
}
|
||||
for i in 0..buf_rcv.len() {
|
||||
buf_rcv[i] = 0x30 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_write_read(0x44, &buf_short, &mut buf_rcv) {
|
||||
|
||||
match i2c.blocking_read(0x48, &mut buf_20) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x48 Read expected to fail!").unwrap();
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(
|
||||
&mut writer,
|
||||
"Test 0x48 Ok. First time, slave did not yet prepare a buffer Error: {:?}",
|
||||
err
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
match i2c.blocking_read(0x49, &mut buf_20) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x49 Read Ok").unwrap();
|
||||
print_buffer(&mut writer, &buf_20);
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x49 Error: {:?}", err).unwrap(),
|
||||
};
|
||||
match i2c.blocking_read(0x4A, &mut buf_64) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4A failed. Expected was a NACK error").unwrap();
|
||||
print_buffer(&mut writer, &buf_64);
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x4A Ok. Expected to fail. Error: {:?}", err).unwrap(),
|
||||
};
|
||||
match i2c.blocking_read(0x4B, &mut buf_64) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4B succeeded: edge case read exact the buffer size").unwrap();
|
||||
print_buffer(&mut writer, &buf_64);
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x4B failed. Error: {:?}", err).unwrap(),
|
||||
};
|
||||
/*
|
||||
match i2c.blocking_write_read(0x44, &buf_20, &mut buf_64) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x44 Ok ").unwrap();
|
||||
writeln!(
|
||||
@ -145,52 +174,7 @@ async fn main(_spawner: Spawner) {
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x44 error: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x48 master read slave write good case: exact 20 characters
|
||||
for i in 0..buf_short.len() {
|
||||
buf_short[i] = 0x61 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_read(0x48, &mut buf_short) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x48 Ok ").unwrap();
|
||||
for i in 0..buf_short.len() {
|
||||
writeln!(&mut writer, "{}", buf_short[i]).unwrap();
|
||||
}
|
||||
writeln!(&mut writer, "").unwrap()
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x48 unexpected error: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x49 master read slave write bad case: master expects 50 slave does send 20 characters
|
||||
for i in 0..buf_long.len() {
|
||||
buf_long[i] = 0x61 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_read(0x49, &mut buf_long) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x49 Ok ").unwrap();
|
||||
for i in 0..buf_long.len() {
|
||||
writeln!(&mut writer, "{}", buf_long[i]).unwrap();
|
||||
}
|
||||
writeln!(&mut writer, "").unwrap()
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x49 error: {:?}", err).unwrap(),
|
||||
};
|
||||
// 0x4A master read slave write bad case: master expects 20 does slave does send 50 characters
|
||||
for i in 0..buf_short.len() {
|
||||
buf_short[i] = 0x41 + (i as u8)
|
||||
}
|
||||
match i2c.blocking_read(0x4A, &mut buf_short) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4A Ok ").unwrap();
|
||||
|
||||
for i in 0..buf_short.len() {
|
||||
writeln!(&mut writer, "{}", buf_short[i]).unwrap();
|
||||
}
|
||||
writeln!(&mut writer, "").unwrap()
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x4A error: {:?}", err).unwrap(),
|
||||
};
|
||||
*/
|
||||
// 0x4F test end and slave will present results
|
||||
let mut result: [u8; 2] = [0, 0];
|
||||
match i2c.blocking_read(0x4F, &mut result) {
|
||||
@ -204,24 +188,12 @@ async fn main(_spawner: Spawner) {
|
||||
Err(err) => writeln!(&mut writer, "Test 0x4F unexpected error: {:?}", err).unwrap(),
|
||||
};
|
||||
writeln!(&mut writer, "").unwrap();
|
||||
let mut joyb = [0_u8; 3];
|
||||
match i2c.blocking_read(0x52, &mut joyb) {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x52 Ok ").unwrap();
|
||||
for i in 0..joyb.len() {
|
||||
writeln!(&mut writer, " {} ", joyb[i]).unwrap();
|
||||
}
|
||||
writeln!(&mut writer, "").unwrap()
|
||||
}
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x52 unexpected error: {:?}", err).unwrap(),
|
||||
};
|
||||
match i2c.blocking_write(0x21, &mut buf_io) {
|
||||
Ok(_) => (),
|
||||
Err(Error::Timeout) => writeln!(&mut writer, "Operation timed out").unwrap(),
|
||||
Err(err) => writeln!(&mut writer, "Test 0x21 unexpected error: {:?}", err).unwrap(),
|
||||
};
|
||||
buf_io[0] += 1;
|
||||
Timer::after(Duration::from_millis(10_000)).await;
|
||||
}
|
||||
fn print_buffer(writer: &mut SerialWriter, buf: &[u8]) {
|
||||
for i in 0..buf.len() {
|
||||
write!(writer, " {:2x} ", buf[i]).unwrap();
|
||||
}
|
||||
writeln!(writer, "\n\r").unwrap()
|
||||
}
|
||||
}
|
||||
|
279
examples/stm32g0/src/bin/i2c_slave.rs
Normal file
279
examples/stm32g0/src/bin/i2c_slave.rs
Normal file
@ -0,0 +1,279 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::i2c::I2c;
|
||||
use embassy_stm32::pac::i2c::vals;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::usart::UartTx;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals, usart};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1 => i2c::InterruptHandler<peripherals::I2C1>;
|
||||
USART1 => usart::InterruptHandler<peripherals::USART1>;
|
||||
});
|
||||
|
||||
macro_rules! checkIsWrite {
|
||||
($writer:ident, $direction:ident) => {
|
||||
match $direction {
|
||||
vals::Dir::WRITE => (),
|
||||
_ => {
|
||||
write!($writer, "Error incorrect direction {:?}\r", $direction as usize).unwrap();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! checkIsRead {
|
||||
($writer:ident, $direction:ident) => {
|
||||
match $direction {
|
||||
vals::Dir::READ => (),
|
||||
_ => {
|
||||
write!($writer, "Error incorrect direction {:?}\r", $direction as usize).unwrap();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct SerialWriter {
|
||||
tx: UartTx<'static, peripherals::USART1, peripherals::DMA1_CH1>,
|
||||
}
|
||||
impl SerialWriter {
|
||||
pub fn new(tx: UartTx<'static, peripherals::USART1, peripherals::DMA1_CH1>) -> Self {
|
||||
SerialWriter { tx }
|
||||
}
|
||||
}
|
||||
impl fmt::Write for SerialWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
_ = self.tx.blocking_write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
|
||||
let uart = usart::Uart::new(
|
||||
p.USART1,
|
||||
p.PB7,
|
||||
p.PB6,
|
||||
Irqs,
|
||||
p.DMA1_CH1,
|
||||
p.DMA1_CH2,
|
||||
usart::Config::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let (tx, _rx) = uart.split();
|
||||
|
||||
let mut writer = SerialWriter::new(tx);
|
||||
|
||||
writeln!(
|
||||
&mut writer,
|
||||
"async i2c slave test. Should be used together with i2c_master test\r"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut config = i2c::Config::default();
|
||||
config.slave_address_7bits(0x4);
|
||||
config.slave_address_2(0x41, vals::Oamsk::MASK4);
|
||||
writeln!(&mut writer, "After config:\r",).unwrap();
|
||||
|
||||
let mut i2c = I2c::new(p.I2C1, p.PB8, p.PB9, Irqs, NoDma, NoDma, Hertz(100_000), config);
|
||||
writeln!(&mut writer, "After i2c init\r").unwrap();
|
||||
|
||||
let mut buf_64 = [0; 64]; // buffer is longer than master will send: wait for STOP condition
|
||||
let mut buf_20 = [0; 20]; // buffer is shorter than master will send: wait for STOP condition
|
||||
let mut errors = 0;
|
||||
let mut address = 0;
|
||||
let mut dir = vals::Dir::READ;
|
||||
let mut tcount = 0;
|
||||
let mut counter = 0;
|
||||
let mut error = Ok((0, vals::Dir::READ, 0));
|
||||
|
||||
// start of the actual test
|
||||
i2c.slave_start_listen().unwrap();
|
||||
loop {
|
||||
counter += 1;
|
||||
writeln!(&mut writer, "Loop: {}\r", counter).unwrap();
|
||||
|
||||
for i in 0..buf_20.len() {
|
||||
buf_20[i] = 0x61 + (i as u8)
|
||||
}
|
||||
for i in 0..buf_64.len() {
|
||||
buf_64[i] = 0x41 + (i as u8)
|
||||
}
|
||||
|
||||
writeln!(&mut writer, "Waiting for master activity\r").unwrap();
|
||||
|
||||
error = Ok((0, vals::Dir::WRITE, 0));
|
||||
match i2c.slave_transaction().await {
|
||||
Ok((taddr, tdir, tsize)) => {
|
||||
address = taddr;
|
||||
dir = tdir;
|
||||
writeln!(
|
||||
&mut writer,
|
||||
"Test ok. Address: x{:2x} dir: {:?} size: x{:2x}\r",
|
||||
taddr, tdir as u8, tsize
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Err(e) => {
|
||||
error = Err(e);
|
||||
writeln!(&mut writer, "Test failed: Error: {:?}", e).unwrap()
|
||||
}
|
||||
}
|
||||
tcount += 1;
|
||||
|
||||
match address {
|
||||
0x41 => {
|
||||
// 0x41 good case master write slave read: master does send 20 bytes slave receives 20 bytes
|
||||
checkIsWrite!(writer, dir);
|
||||
_ = i2c.slave_read_buffer(&mut buf_20, i2c::AddressType::GenericAddress);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x41 Ok. send 20 bytes").unwrap();
|
||||
print_buffer(&mut writer, &buf_20);
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x41 failed. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
0x42 => {
|
||||
// 0x42 good case edge case: exact 64 bytes master write slave read:
|
||||
checkIsWrite!(writer, dir);
|
||||
_ = i2c.slave_read_buffer(&mut buf_64, i2c::AddressType::GenericAddress);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x42 Ok. send 64 bytes").unwrap();
|
||||
print_buffer(&mut writer, &buf_64);
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x42 failed. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
0x43 => {
|
||||
// 0x43 bad case master write slave read: master does send more than 64 bytes, slave does NACK
|
||||
checkIsWrite!(writer, dir);
|
||||
_ = i2c.slave_read_buffer(&mut buf_64, i2c::AddressType::GenericAddress);
|
||||
match i2c.slave_read_buffer(&mut buf_64, i2c::AddressType::GenericAddress) {
|
||||
Ok(_) => {
|
||||
{
|
||||
writeln!(&mut writer, "Test 0x43 failed. Expected to fail. with FrameError\r").unwrap()
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x43 Ok Expected error. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
0x48 => {
|
||||
// 0x48 master read slave write slave did not yet prepare a buffer, master will fail
|
||||
checkIsRead!(writer, dir);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x48 fail expected to fail: \r").unwrap();
|
||||
errors += 1;
|
||||
}
|
||||
Err(err) => writeln!(&mut writer, "Test 0x48 Ok. Expected error: {:?}\r", err).unwrap(),
|
||||
};
|
||||
// prepare buffer for next round
|
||||
for i in 0..buf_20.len() {
|
||||
buf_20[i] = 0x61 + (i as u8)
|
||||
}
|
||||
_ = i2c.slave_write_buffer(&buf_20, i2c::AddressType::GenericAddress);
|
||||
}
|
||||
0x49 => {
|
||||
// 0x49 master read slave write bad case: master expects 50 does slave does send 20 characters
|
||||
checkIsRead!(writer, dir);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x49 Ok. master did read 20 bytes").unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x49 failed. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
// prepare buffer for next round
|
||||
for i in 0..buf_20.len() {
|
||||
buf_20[i] = 0x41 + (i as u8)
|
||||
}
|
||||
_ = i2c.slave_write_buffer(&buf_20, i2c::AddressType::GenericAddress);
|
||||
}
|
||||
0x4A => {
|
||||
// 0x4A master read slave write bad case: master expects 64 does slave does prepare 20 characters
|
||||
checkIsRead!(writer, dir);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4A failed . Expected an errpr").unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x4A ok. Expected errore rror: {:?}\r", err).unwrap()
|
||||
}
|
||||
}
|
||||
_ = i2c.slave_write_buffer(&buf_20, i2c::AddressType::GenericAddress);
|
||||
// prepare buffer for next round
|
||||
for i in 0..buf_64.len() {
|
||||
buf_64[i] = 0x41 + (i as u8)
|
||||
}
|
||||
_ = i2c.slave_write_buffer(&buf_64, i2c::AddressType::GenericAddress);
|
||||
}
|
||||
0x4B => {
|
||||
// 0x4B master write_read good case each 20 chars
|
||||
checkIsRead!(writer, dir);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4B Ok\r").unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x4B failed. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
let result: [u8; 2] = [tcount, errors];
|
||||
_ = i2c.slave_write_buffer(&result, i2c::AddressType::GenericAddress);
|
||||
}
|
||||
0x4F => {
|
||||
checkIsRead!(writer, dir);
|
||||
match error {
|
||||
Ok(_) => {
|
||||
writeln!(&mut writer, "Test 0x4F Result send to master\r").unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
errors += 1;
|
||||
writeln!(&mut writer, "Test 0x4F failed. Error: {:?}\r", err).unwrap()
|
||||
}
|
||||
};
|
||||
writeln!(
|
||||
&mut writer,
|
||||
"Test finished. nr tests/nr errors: {}/{}!\r",
|
||||
tcount, errors
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(&mut writer, "-----\r").unwrap();
|
||||
tcount = 0;
|
||||
errors = 0;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn print_buffer(writer: &mut SerialWriter, buf: &[u8]) {
|
||||
for i in 0..buf.len() {
|
||||
write!(writer, " {:2x} ", buf[i]).unwrap();
|
||||
}
|
||||
writeln!(writer, "\n\r").unwrap()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user