From 26e37c673dbd945987ce2d481364d26f028c8c96 Mon Sep 17 00:00:00 2001 From: anton smeenk Date: Sat, 28 Oct 2023 08:25:41 +0200 Subject: [PATCH] Added public interfaces for vals::Dir and vals::Oamsk, so they can be used outside the crate --- embassy-stm32/src/i2c/mod.rs | 46 ++++++++++++++ embassy-stm32/src/i2c/v2.rs | 60 +++++++++++++------ examples/stm32g0/src/bin/i2c_slave.rs | 14 ++--- .../stm32g0/src/bin/i2c_slave_arbitration.rs | 32 +--------- 4 files changed, 97 insertions(+), 55 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 5358106f..6e0773d4 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +use stm32_metapac::i2c::vals; + use crate::interrupt; #[cfg_attr(i2c_v1, path = "v1.rs")] @@ -29,6 +31,50 @@ pub enum AddressType { Address2, } +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum Address2Mask { + NOMASK, + MASK1, + MASK2, + MASK3, + MASK4, + MASK5, + MASK6, + MASK7, +} +impl Address2Mask { + #[inline(always)] + pub const fn to_vals_impl(self) -> vals::Oamsk { + match self { + Address2Mask::NOMASK => vals::Oamsk::NOMASK, + Address2Mask::MASK1 => vals::Oamsk::MASK1, + Address2Mask::MASK2 => vals::Oamsk::MASK2, + Address2Mask::MASK3 => vals::Oamsk::MASK3, + Address2Mask::MASK4 => vals::Oamsk::MASK4, + Address2Mask::MASK5 => vals::Oamsk::MASK5, + Address2Mask::MASK6 => vals::Oamsk::MASK6, + Address2Mask::MASK7 => vals::Oamsk::MASK7, + } + } +} + +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum Dir { + WRITE, + READ, +} +impl Dir { + #[inline(always)] + pub const fn to_vals_impl(self) -> vals::Dir { + match self { + Dir::READ => vals::Dir::READ, + Dir::WRITE => vals::Dir::WRITE, + } + } +} + pub(crate) mod sealed { use super::*; pub trait Instance: crate::rcc::RccPeripheral { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7933f692..63473a96 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -22,7 +22,7 @@ use crate::dma::NoDma; use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Pull; -use crate::i2c::{AddressType, Error, Instance, SclPin, SdaPin}; +use crate::i2c::{Address2Mask, AddressType, Dir, Error, Instance, SclPin, SdaPin}; use crate::interrupt::typelevel::Interrupt; use crate::pac::i2c; use crate::time::Hertz; @@ -48,8 +48,8 @@ impl interrupt::typelevel::Handler for InterruptHandl }); state_m.result = Some(Error::Bus); } else if isr.arlo() { - regs.icr().write(|w| w.set_arlocf(true)); state_m.result = Some(Error::Arbitration); + regs.icr().write(|w| w.set_arlocf(true)); } else if isr.nackf() { regs.icr().write(|w| w.set_nackcf(true)); } else if isr.txis() { @@ -78,9 +78,12 @@ impl interrupt::typelevel::Handler for InterruptHandl } } else if isr.stopf() { // Clear the stop condition flag - regs.icr().write(|w| w.set_stopcf(true)); state_m.ready = true; + // make a copy of the current state as result of the transaction + state_m.transaction_result = + (state_m.current_address, state_m.dir, state_m.get_size(), state_m.result); T::state().waker.wake(); + regs.icr().write(|w| w.set_stopcf(true)); } 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 @@ -91,10 +94,10 @@ impl interrupt::typelevel::Handler for InterruptHandl } 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(); + state_m.dir = if isr.dir() as u8 == 0 { Dir::WRITE } else { Dir::READ }; state_m.result = None; - if state_m.dir == vals::Dir::READ { + if state_m.dir == 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 @@ -153,7 +156,7 @@ pub struct Config { pub scl_pullup: bool, pub slave_address_1: u16, pub slave_address_2: u8, - pub slave_address_mask: vals::Oamsk, + pub slave_address_mask: Address2Mask, pub address_11bits: bool, #[cfg(feature = "time")] pub transaction_timeout: Duration, @@ -173,7 +176,7 @@ impl Config { } /// 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) { + pub fn slave_address_2(&mut self, address: u8, mask: Address2Mask) { // assert!(address < (2 ^ 7)); self.slave_address_2 = address; self.slave_address_mask = mask; @@ -186,7 +189,7 @@ impl Default for Config { scl_pullup: false, slave_address_1: 0, slave_address_2: 0, - slave_address_mask: vals::Oamsk::NOMASK, + slave_address_mask: Address2Mask::NOMASK, address_11bits: false, #[cfg(feature = "time")] transaction_timeout: Duration::from_millis(100), @@ -214,7 +217,10 @@ struct I2cStateMachine { address1: u16, ready: bool, current_address: u8, - dir: vals::Dir, + dir: Dir, + // at the end of the transaction make a copy of the result + // to prevent corruption if a new transaction starts immediatly + transaction_result: (u8, Dir, u8, Option), } impl I2cStateMachine { pub(crate) const fn new() -> Self { @@ -228,8 +234,9 @@ impl I2cStateMachine { slave_mode: false, address1: 0, current_address: 0, - dir: vals::Dir::READ, + dir: Dir::READ, ready: false, + transaction_result: (0, Dir::READ, 0, None), } } fn read_byte(&mut self) -> Result { @@ -238,7 +245,7 @@ impl I2cStateMachine { } else { AddressType::Address2 }; - self.buffers[adress_type as usize][vals::Dir::READ as usize].master_read() + self.buffers[adress_type as usize][Dir::READ as usize].master_read() } fn write_byte(&mut self, b: u8) -> Result<(), Error> { let adress_type = if self.address1 == self.current_address as u16 { @@ -246,7 +253,7 @@ impl I2cStateMachine { } else { AddressType::Address2 }; - self.buffers[adress_type as usize][vals::Dir::WRITE as usize].master_write(b) + self.buffers[adress_type as usize][Dir::WRITE as usize].master_write(b) } fn get_size(&self) -> u8 { let adress_type = if self.address1 == self.current_address as u16 { @@ -420,7 +427,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { reg.set_oa2en(false); }); T::regs().oar2().write(|reg| { - reg.set_oa2msk(config.slave_address_mask); + reg.set_oa2msk(config.slave_address_mask.to_vals_impl()); reg.set_oa2(config.slave_address_2); reg.set_oa2en(true); }); @@ -1079,6 +1086,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }); Ok(()) } + + pub fn set_address_1(&self, address7: u8) -> Result<(), Error> { + T::regs().oar1().modify(|reg| { + reg.set_oa1en(false); + }); + T::regs().oar1().modify(|reg| { + reg.set_oa1(address7 as u16); + reg.set_oa1en(true); + }); + T::state().mutex.lock(|f| { + let mut state_m = f.borrow_mut(); + state_m.address1 = address7 as u16; + }); + Ok(()) + } pub fn slave_sbc(&self, sbc_enabled: bool) { // enable acknowlidge control T::regs().cr1().modify(|w| w.set_sbc(sbc_enabled)); @@ -1088,16 +1110,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn slave_write_buffer(&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]; + let buf = &mut state_m.buffers[address_type as usize][Dir::READ as usize]; buf.from_buffer(buffer) }) } pub fn slave_reset_buffer(&self, address_type: AddressType) { T::state().mutex.lock(|f| { let mut state_m = f.borrow_mut(); - let buf_r = &mut state_m.buffers[address_type as usize][vals::Dir::READ as usize]; + let buf_r = &mut state_m.buffers[address_type as usize][Dir::READ as usize]; buf_r.reset(); - let buf_w = &mut state_m.buffers[address_type as usize][vals::Dir::WRITE as usize]; + let buf_w = &mut state_m.buffers[address_type as usize][Dir::WRITE as usize]; buf_w.reset(); }) } @@ -1107,12 +1129,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn slave_read_buffer(&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]; + let buf = &mut state_m.buffers[address_type as usize][Dir::WRITE as usize]; buf.to_buffer(buffer) }) } /// wait until a slave transaction is finished, and return tuple address, direction, data size and error - pub async fn slave_transaction(&self) -> (u8, vals::Dir, u8, Option) { + pub async fn slave_transaction(&self) -> (u8, Dir, u8, Option) { // async wait until addressed poll_fn(|cx| { T::state().waker.register(cx.waker()); @@ -1120,7 +1142,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let mut state_m = f.borrow_mut(); if state_m.ready { state_m.ready = false; - return Poll::Ready((state_m.current_address, state_m.dir, state_m.get_size(), state_m.result)); + return Poll::Ready(state_m.transaction_result); } else { return Poll::Pending; } diff --git a/examples/stm32g0/src/bin/i2c_slave.rs b/examples/stm32g0/src/bin/i2c_slave.rs index b49b4f28..3bc391a2 100644 --- a/examples/stm32g0/src/bin/i2c_slave.rs +++ b/examples/stm32g0/src/bin/i2c_slave.rs @@ -9,8 +9,7 @@ use core::fmt::{self, Write}; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::i2c::{Error, I2c}; -use embassy_stm32::pac::i2c::vals; +use embassy_stm32::i2c::{Address2Mask, Dir, Error, I2c}; use embassy_stm32::time::Hertz; use embassy_stm32::usart::UartTx; use embassy_stm32::{bind_interrupts, i2c, peripherals, usart}; @@ -25,7 +24,7 @@ bind_interrupts!(struct Irqs { macro_rules! checkIsWrite { ($writer:ident, $direction:ident) => { match $direction { - vals::Dir::WRITE => (), + Dir::WRITE => (), _ => { write!($writer, "Error incorrect direction {:?}\r", $direction as usize).unwrap(); continue; @@ -36,7 +35,7 @@ macro_rules! checkIsWrite { macro_rules! checkIsRead { ($writer:ident, $direction:ident) => { match $direction { - vals::Dir::READ => (), + Dir::READ => (), _ => { write!($writer, "Error incorrect direction {:?}\r", $direction as usize).unwrap(); continue; @@ -108,7 +107,7 @@ async fn main(spawner: Spawner) { let mut config = i2c::Config::default(); config.slave_address_7bits(0x10); // for arbitration lost test - config.slave_address_2(0x41, vals::Oamsk::MASK4); + config.slave_address_2(0x41, Address2Mask::MASK4); let i2c = I2c::new(p.I2C1, p.PB8, p.PB9, Irqs, NoDma, NoDma, Hertz(100_000), config); @@ -116,9 +115,10 @@ async fn main(spawner: Spawner) { let mut buf_20 = [0; 20]; // buffer is shorter than master will send: wait for STOP condition let mut buf_20a = [0; 20]; let mut buf_2 = [0; 2]; + let mut buf_1 = [0; 1]; let mut errors = 0; let mut address = 0; - let mut dir = vals::Dir::READ; + let mut dir = Dir::READ; let mut tcount = 0; let mut counter = 0; let mut result: Option = None; @@ -360,7 +360,7 @@ async fn main(spawner: Spawner) { tcount = 0; errors = 0; } - 0x10 => { + 0x4 => { // Arbitration lost test Master does read 2 bytes on address 0x10 // this slave will send 0xFF04, the other slave will send 0xFF03 // This slave should generate a arbitration lost if the other slave is online diff --git a/examples/stm32g0/src/bin/i2c_slave_arbitration.rs b/examples/stm32g0/src/bin/i2c_slave_arbitration.rs index 70d7dd43..b2f7e1db 100644 --- a/examples/stm32g0/src/bin/i2c_slave_arbitration.rs +++ b/examples/stm32g0/src/bin/i2c_slave_arbitration.rs @@ -10,12 +10,10 @@ use core::fmt::{self, Write}; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::i2c::{Error, I2c}; -use embassy_stm32::pac::i2c::vals; +use embassy_stm32::i2c::{Dir, Error, I2c}; use embassy_stm32::time::Hertz; use embassy_stm32::usart::UartTx; use embassy_stm32::{bind_interrupts, i2c, peripherals, usart}; -use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -23,29 +21,6 @@ bind_interrupts!(struct Irqs { USART1 => usart::InterruptHandler; }); -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>, } @@ -106,7 +81,7 @@ async fn main(spawner: Spawner) { let mut buf_2 = [0; 2]; let mut address = 0; - let mut dir = vals::Dir::READ; + let mut dir = Dir::READ; let mut counter = 0; let mut result: Option = None; @@ -119,7 +94,7 @@ async fn main(spawner: Spawner) { // content for test 0x10 buf_2[0] = 0xFF; buf_2[1] = 0x03; - _ = i2c.slave_write_buffer(&mut buf_2, i2c::AddressType::MainAddress); + _ = i2c.slave_write_buffer(&mut buf_2, i2c::AddressType::Address1); writeln!(&mut writer, "Waiting for master activity\r").unwrap(); @@ -137,7 +112,6 @@ async fn main(spawner: Spawner) { // this slave will send 0xFF03, the other slave will send 0xFF04 // This slave should win , so no error here writeln!(&mut writer, "Evaluate arbitration lost test 0x10.\r\n").unwrap(); - checkIsRead!(writer, dir); match result { None => { writeln!(&mut writer, "Test 0x10 Passed\n\r").unwrap();