mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
add stm32 i2c slave example
This commit is contained in:
parent
bfc162d437
commit
fc342915e6
@ -70,19 +70,19 @@ pub mod mode {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The command kind to the slave from the master
|
||||
pub enum CommandKind {
|
||||
pub enum SlaveCommandKind {
|
||||
/// Write to the slave
|
||||
SlaveReceive,
|
||||
Write,
|
||||
/// Read from the slave
|
||||
SlaveSend,
|
||||
Read,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The command kind to the slave from the master and the address that the slave matched
|
||||
pub struct Command {
|
||||
pub struct SlaveCommand {
|
||||
/// The kind of command
|
||||
pub kind: CommandKind,
|
||||
pub kind: SlaveCommandKind,
|
||||
/// The address that the slave matched
|
||||
pub address: Address,
|
||||
}
|
||||
|
@ -887,7 +887,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
||||
/// Listen for incoming I2C messages.
|
||||
///
|
||||
/// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
|
||||
pub async fn listen(&mut self) -> Result<Command, Error> {
|
||||
pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
|
||||
let state = self.state;
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_addrie(true);
|
||||
@ -902,12 +902,12 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
||||
// we do not clear the address flag here as it will be cleared by the dma read/write
|
||||
// if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
|
||||
match isr.dir() {
|
||||
i2c::vals::Dir::WRITE => Poll::Ready(Ok(Command {
|
||||
kind: CommandKind::SlaveReceive,
|
||||
i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand {
|
||||
kind: SlaveCommandKind::Write,
|
||||
address: self.determine_matched_address()?,
|
||||
})),
|
||||
i2c::vals::Dir::READ => Poll::Ready(Ok(Command {
|
||||
kind: CommandKind::SlaveSend,
|
||||
i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand {
|
||||
kind: SlaveCommandKind::Read,
|
||||
address: self.determine_matched_address()?,
|
||||
})),
|
||||
}
|
||||
@ -916,30 +916,30 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Respond to a receive command.
|
||||
pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> {
|
||||
/// Respond to a write command.
|
||||
pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
self.slave_read_internal(read, timeout)
|
||||
}
|
||||
|
||||
/// Respond to a send command.
|
||||
pub fn blocking_respond_to_send(&mut self, write: &[u8]) -> Result<(), Error> {
|
||||
/// Respond to a read command.
|
||||
pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
self.slave_write_internal(write, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Async, MultiMaster> {
|
||||
/// Respond to a receive command.
|
||||
/// Respond to a write command.
|
||||
///
|
||||
/// Returns the total number of bytes received.
|
||||
pub async fn respond_to_receive(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let timeout = self.timeout();
|
||||
timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
|
||||
}
|
||||
|
||||
/// Respond to a send request from an I2C master.
|
||||
pub async fn respond_to_send(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
|
||||
/// Respond to a read request from an I2C master.
|
||||
pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
|
||||
let timeout = self.timeout();
|
||||
timeout.with(self.write_dma_internal_slave(write, timeout)).await
|
||||
}
|
||||
|
149
examples/stm32g4/src/bin/i2c_slave.rs
Normal file
149
examples/stm32g4/src/bin/i2c_slave.rs
Normal file
@ -0,0 +1,149 @@
|
||||
//! This example shows how to use an stm32 as both a master and a slave.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind};
|
||||
use embassy_stm32::mode::Async;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
const DEV_ADDR: u8 = 0x42;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! {
|
||||
info!("Device start");
|
||||
|
||||
let mut state = 0;
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8; 128];
|
||||
match dev.listen().await {
|
||||
Ok(i2c::SlaveCommand {
|
||||
kind: SlaveCommandKind::Read,
|
||||
address: Address::SevenBit(DEV_ADDR),
|
||||
}) => match dev.respond_to_read(&[state]).await {
|
||||
Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x),
|
||||
Ok(i2c::SendStatus::Done) => {}
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
},
|
||||
Ok(i2c::SlaveCommand {
|
||||
kind: SlaveCommandKind::Write,
|
||||
address: Address::SevenBit(DEV_ADDR),
|
||||
}) => match dev.respond_to_write(&mut buf).await {
|
||||
Ok(len) => {
|
||||
info!("Device received write: {}", buf[..len]);
|
||||
|
||||
if match buf[0] {
|
||||
// Set the state
|
||||
0xC2 => {
|
||||
state = buf[1];
|
||||
true
|
||||
}
|
||||
// Reset State
|
||||
0xC8 => {
|
||||
state = 0;
|
||||
true
|
||||
}
|
||||
x => {
|
||||
error!("Invalid Write Read {:x}", x);
|
||||
false
|
||||
}
|
||||
} {
|
||||
match dev.respond_to_read(&[state]).await {
|
||||
Ok(read_status) => info!(
|
||||
"This read is part of a write/read transaction. The response read status {}",
|
||||
read_status
|
||||
),
|
||||
Err(i2c::Error::Timeout) => {
|
||||
info!("The device only performed a write and it not also do a read")
|
||||
}
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("error while receiving {}", e),
|
||||
},
|
||||
Ok(i2c::SlaveCommand { address, .. }) => {
|
||||
defmt::unreachable!(
|
||||
"The slave matched address: {}, which it was not configured for",
|
||||
address
|
||||
);
|
||||
}
|
||||
Err(e) => error!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) {
|
||||
info!("Controller start");
|
||||
|
||||
loop {
|
||||
let mut resp_buff = [0u8; 1];
|
||||
for i in 0..10 {
|
||||
match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("write_read response: {}", resp_buff);
|
||||
defmt::assert_eq!(resp_buff[0], i);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
match con.read(DEV_ADDR, &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("read response: {}", resp_buff);
|
||||
// assert that the state is the last index that was written
|
||||
defmt::assert_eq!(resp_buff[0], 9);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("write_read response: {}", resp_buff);
|
||||
// assert that the state has been reset
|
||||
defmt::assert_eq!(resp_buff[0], 0);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let speed = Hertz::khz(400);
|
||||
let config = i2c::Config::default();
|
||||
|
||||
let d_addr_config = i2c::SlaveAddrConfig {
|
||||
addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)),
|
||||
general_call: false,
|
||||
};
|
||||
let d_sda = p.PA8;
|
||||
let d_scl = p.PA9;
|
||||
let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config)
|
||||
.into_slave_multimaster(d_addr_config);
|
||||
|
||||
unwrap!(spawner.spawn(device_task(device)));
|
||||
|
||||
let c_sda = p.PB8;
|
||||
let c_scl = p.PB7;
|
||||
let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config);
|
||||
|
||||
unwrap!(spawner.spawn(controller_task(controller)));
|
||||
}
|
Loading…
Reference in New Issue
Block a user