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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// The command kind to the slave from the master
|
/// The command kind to the slave from the master
|
||||||
pub enum CommandKind {
|
pub enum SlaveCommandKind {
|
||||||
/// Write to the slave
|
/// Write to the slave
|
||||||
SlaveReceive,
|
Write,
|
||||||
/// Read from the slave
|
/// Read from the slave
|
||||||
SlaveSend,
|
Read,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// The command kind to the slave from the master and the address that the slave matched
|
/// 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
|
/// The kind of command
|
||||||
pub kind: CommandKind,
|
pub kind: SlaveCommandKind,
|
||||||
/// The address that the slave matched
|
/// The address that the slave matched
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -887,7 +887,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
|||||||
/// Listen for incoming I2C messages.
|
/// Listen for incoming I2C messages.
|
||||||
///
|
///
|
||||||
/// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
|
/// 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;
|
let state = self.state;
|
||||||
self.info.regs.cr1().modify(|reg| {
|
self.info.regs.cr1().modify(|reg| {
|
||||||
reg.set_addrie(true);
|
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
|
// 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
|
// 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() {
|
match isr.dir() {
|
||||||
i2c::vals::Dir::WRITE => Poll::Ready(Ok(Command {
|
i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand {
|
||||||
kind: CommandKind::SlaveReceive,
|
kind: SlaveCommandKind::Write,
|
||||||
address: self.determine_matched_address()?,
|
address: self.determine_matched_address()?,
|
||||||
})),
|
})),
|
||||||
i2c::vals::Dir::READ => Poll::Ready(Ok(Command {
|
i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand {
|
||||||
kind: CommandKind::SlaveSend,
|
kind: SlaveCommandKind::Read,
|
||||||
address: self.determine_matched_address()?,
|
address: self.determine_matched_address()?,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
@ -916,30 +916,30 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Respond to a receive command.
|
/// Respond to a write command.
|
||||||
pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
self.slave_read_internal(read, timeout)
|
self.slave_read_internal(read, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Respond to a send command.
|
/// Respond to a read command.
|
||||||
pub fn blocking_respond_to_send(&mut self, write: &[u8]) -> Result<(), Error> {
|
pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
self.slave_write_internal(write, timeout)
|
self.slave_write_internal(write, timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> I2c<'d, Async, MultiMaster> {
|
impl<'d> I2c<'d, Async, MultiMaster> {
|
||||||
/// Respond to a receive command.
|
/// Respond to a write command.
|
||||||
///
|
///
|
||||||
/// Returns the total number of bytes received.
|
/// 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();
|
let timeout = self.timeout();
|
||||||
timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
|
timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Respond to a send request from an I2C master.
|
/// Respond to a read request from an I2C master.
|
||||||
pub async fn respond_to_send(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
|
pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
timeout.with(self.write_dma_internal_slave(write, timeout)).await
|
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…
x
Reference in New Issue
Block a user