Merge remote-tracking branch 'origin/main' into nrf-pdm
This commit is contained in:
commit
42de1c3a06
41
.gitattributes
vendored
Normal file
41
.gitattributes
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
* text=auto
|
||||
|
||||
*.adoc text
|
||||
*.html text
|
||||
*.in text
|
||||
*.json text
|
||||
*.md text
|
||||
*.proto text
|
||||
*.py text
|
||||
*.rs text
|
||||
*.service text
|
||||
*.sh text
|
||||
*.toml text
|
||||
*.txt text
|
||||
*.x text
|
||||
*.yml text
|
||||
|
||||
*.raw binary
|
||||
*.bin binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.mov binary
|
||||
*.mp4 binary
|
||||
*.mp3 binary
|
||||
*.flv binary
|
||||
*.fla binary
|
||||
*.swf binary
|
||||
*.gz binary
|
||||
*.zip binary
|
||||
*.7z binary
|
||||
*.ttf binary
|
||||
*.eot binary
|
||||
*.woff binary
|
||||
*.pyc binary
|
||||
*.pdf binary
|
||||
*.ez binary
|
||||
*.bz2 binary
|
||||
*.swp binary
|
17
.github/ci/crlf.sh
vendored
Executable file
17
.github/ci/crlf.sh
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
## on push branch~=gh-readonly-queue/main/.*
|
||||
## on pull_request
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
|
||||
|
||||
if [ -z "$FILES_WITH_CRLF" ]; then
|
||||
echo -e "No files with CRLF endings found."
|
||||
exit 0
|
||||
else
|
||||
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
|
||||
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
|
||||
echo "$FILES_WITH_CRLF"
|
||||
exit "$NR_FILES"
|
||||
fi
|
1
.github/ci/doc.sh
vendored
1
.github/ci/doc.sh
vendored
@ -37,6 +37,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g
|
||||
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
|
||||
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
||||
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||
|
||||
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||
|
@ -18,3 +18,9 @@ embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||
heapless = "0.7.16"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
|
||||
target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
@ -479,30 +479,78 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "igmp")]
|
||||
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
|
||||
impl<D: Driver + 'static> Stack<D> {
|
||||
/// Join a multicast group.
|
||||
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await
|
||||
}
|
||||
|
||||
/// Join a multicast group.
|
||||
///
|
||||
/// When the send queue is full, this method will return `Poll::Pending`
|
||||
/// and register the current task to be notified when the queue has space available.
|
||||
pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
self.with_mut(|s, i| {
|
||||
s.iface
|
||||
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
||||
let mut smoldev = DriverAdapter {
|
||||
cx: Some(cx),
|
||||
inner: &mut i.device,
|
||||
};
|
||||
|
||||
match s
|
||||
.iface
|
||||
.join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||
{
|
||||
Ok(announce_sent) => Poll::Ready(Ok(announce_sent)),
|
||||
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||
Err(other) => Poll::Ready(Err(other)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Leave a multicast group.
|
||||
pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await
|
||||
}
|
||||
|
||||
/// Leave a multicast group.
|
||||
///
|
||||
/// When the send queue is full, this method will return `Poll::Pending`
|
||||
/// and register the current task to be notified when the queue has space available.
|
||||
pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
self.with_mut(|s, i| {
|
||||
s.iface
|
||||
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
||||
let mut smoldev = DriverAdapter {
|
||||
cx: Some(cx),
|
||||
inner: &mut i.device,
|
||||
};
|
||||
|
||||
match s
|
||||
.iface
|
||||
.leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||
{
|
||||
Ok(leave_sent) => Poll::Ready(Ok(leave_sent)),
|
||||
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||
Err(other) => Poll::Ready(Err(other)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -531,11 +579,14 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
|
||||
debug!(" IP address: {}", config.address);
|
||||
s.iface.update_ip_addrs(|addrs| {
|
||||
if addrs.is_empty() {
|
||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||
} else {
|
||||
addrs[0] = IpCidr::Ipv4(config.address);
|
||||
if let Some((index, _)) = addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||
{
|
||||
addrs.remove(index);
|
||||
}
|
||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||
});
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
@ -570,11 +621,14 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
|
||||
debug!(" IP address: {}", config.address);
|
||||
s.iface.update_ip_addrs(|addrs| {
|
||||
if addrs.is_empty() {
|
||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||
} else {
|
||||
addrs[0] = IpCidr::Ipv6(config.address);
|
||||
if let Some((index, _)) = addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
|
||||
{
|
||||
addrs.remove(index);
|
||||
}
|
||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||
});
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
@ -642,13 +696,21 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
socket.set_retry_config(config.retry_config);
|
||||
}
|
||||
|
||||
#[allow(unused)] // used only with dhcp
|
||||
fn unapply_config(&mut self, s: &mut SocketStack) {
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
let medium = self.device.capabilities().medium;
|
||||
|
||||
debug!("Lost IP configuration");
|
||||
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
|
||||
s.iface.update_ip_addrs(|ip_addrs| {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
if let Some((index, _)) = ip_addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||
{
|
||||
ip_addrs.remove(index);
|
||||
}
|
||||
});
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
if medium == Medium::Ethernet {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
@ -695,7 +757,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
if self.link_up {
|
||||
match socket.poll() {
|
||||
None => {}
|
||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
|
||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
|
||||
Some(dhcpv4::Event::Configured(config)) => {
|
||||
let config = StaticConfigV4 {
|
||||
address: config.address,
|
||||
@ -707,7 +769,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
}
|
||||
} else if old_link_up {
|
||||
socket.reset();
|
||||
self.unapply_config(s);
|
||||
self.unapply_config_v4(s);
|
||||
}
|
||||
}
|
||||
//if old_link_up || self.link_up {
|
||||
|
@ -716,6 +716,9 @@ mod nightly {
|
||||
async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
|
||||
let addr: u16 = address.into();
|
||||
|
||||
if operations.len() > 0 {
|
||||
Self::setup(addr)?;
|
||||
}
|
||||
let mut iterator = operations.iter_mut();
|
||||
|
||||
while let Some(op) = iterator.next() {
|
||||
@ -723,11 +726,9 @@ mod nightly {
|
||||
|
||||
match op {
|
||||
Operation::Read(buffer) => {
|
||||
Self::setup(addr)?;
|
||||
self.read_async_internal(buffer, false, last).await?;
|
||||
}
|
||||
Operation::Write(buffer) => {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
|
||||
}
|
||||
}
|
||||
|
@ -45,20 +45,19 @@ use self::phy_consts::*;
|
||||
pub struct GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration,
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration::from_millis(500),
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "time"))]
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl PHY for GenericSMI {
|
||||
@ -102,6 +101,7 @@ unsafe impl PHY for GenericSMI {
|
||||
|
||||
/// Public functions for the PHY
|
||||
impl GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
|
||||
self.poll_interval = poll_interval
|
||||
}
|
||||
|
@ -1,332 +1,332 @@
|
||||
#![macro_use]
|
||||
|
||||
pub mod enums;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use enums::*;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
pub struct TransferConfig {
|
||||
/// Instraction width (IMODE)
|
||||
pub iwidth: QspiWidth,
|
||||
/// Address width (ADMODE)
|
||||
pub awidth: QspiWidth,
|
||||
/// Data width (DMODE)
|
||||
pub dwidth: QspiWidth,
|
||||
/// Instruction Id
|
||||
pub instruction: u8,
|
||||
/// Flash memory address
|
||||
pub address: Option<u32>,
|
||||
/// Number of dummy cycles (DCYC)
|
||||
pub dummy: DummyCycles,
|
||||
/// Length of data
|
||||
pub data_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TransferConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iwidth: QspiWidth::NONE,
|
||||
awidth: QspiWidth::NONE,
|
||||
dwidth: QspiWidth::NONE,
|
||||
instruction: 0,
|
||||
address: None,
|
||||
dummy: DummyCycles::_0,
|
||||
data_len: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
||||
/// If you need other value the whose predefined use `Other` variant.
|
||||
pub memory_size: MemorySize,
|
||||
/// Address size (8/16/24/32-bit)
|
||||
pub address_size: AddressSize,
|
||||
/// Scalar factor for generating CLK [0-255]
|
||||
pub prescaler: u8,
|
||||
/// Number of bytes to trigger FIFO threshold flag.
|
||||
pub fifo_threshold: FIFOThresholdLevel,
|
||||
/// Minimum number of cycles that chip select must be high between issued commands
|
||||
pub cs_high_time: ChipSelectHightTime,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
memory_size: MemorySize::Other(0),
|
||||
address_size: AddressSize::_24bit,
|
||||
prescaler: 128,
|
||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Qspi<'d, T: Instance, Dma> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
peri,
|
||||
Some(d0.map_into()),
|
||||
Some(d1.map_into()),
|
||||
Some(d2.map_into()),
|
||||
Some(d3.map_into()),
|
||||
Some(sck.map_into()),
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
|
||||
T::enable();
|
||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
T::REGS.cr().write(|w| {
|
||||
w.set_prescaler(config.prescaler);
|
||||
w.set_en(true);
|
||||
});
|
||||
T::REGS.dcr().write(|w| {
|
||||
w.set_fsize(config.memory_size.into());
|
||||
w.set_csht(config.cs_high_time.into());
|
||||
w.set_ckmode(false);
|
||||
});
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
sck,
|
||||
d0,
|
||||
d1,
|
||||
d2,
|
||||
d3,
|
||||
nss,
|
||||
dma,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(&mut self, transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
||||
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().ftf() {}
|
||||
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
buf,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_write(
|
||||
&mut self.dma,
|
||||
request,
|
||||
buf,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
||||
T::REGS.fcr().modify(|v| {
|
||||
v.set_csmf(true);
|
||||
v.set_ctcf(true);
|
||||
v.set_ctef(true);
|
||||
v.set_ctof(true);
|
||||
});
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
||||
}
|
||||
|
||||
T::REGS.ccr().write(|v| {
|
||||
v.set_fmode(fmode.into());
|
||||
v.set_imode(transaction.iwidth.into());
|
||||
v.set_instruction(transaction.instruction);
|
||||
v.set_admode(transaction.awidth.into());
|
||||
v.set_adsize(self.config.address_size.into());
|
||||
v.set_dmode(transaction.dwidth.into());
|
||||
v.set_abmode(QspiWidth::NONE.into());
|
||||
v.set_dcyc(transaction.dummy.into());
|
||||
});
|
||||
|
||||
if let Some(addr) = transaction.address {
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
const REGS: Regs;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
|
||||
pin_trait!(SckPin, Instance);
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(NSSPin, Instance);
|
||||
|
||||
dma_trait!(QuadDma, Instance);
|
||||
|
||||
foreach_peripheral!(
|
||||
(quadspi, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGS: Regs = crate::pac::$inst;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
#![macro_use]
|
||||
|
||||
pub mod enums;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use enums::*;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
pub struct TransferConfig {
|
||||
/// Instraction width (IMODE)
|
||||
pub iwidth: QspiWidth,
|
||||
/// Address width (ADMODE)
|
||||
pub awidth: QspiWidth,
|
||||
/// Data width (DMODE)
|
||||
pub dwidth: QspiWidth,
|
||||
/// Instruction Id
|
||||
pub instruction: u8,
|
||||
/// Flash memory address
|
||||
pub address: Option<u32>,
|
||||
/// Number of dummy cycles (DCYC)
|
||||
pub dummy: DummyCycles,
|
||||
/// Length of data
|
||||
pub data_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TransferConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iwidth: QspiWidth::NONE,
|
||||
awidth: QspiWidth::NONE,
|
||||
dwidth: QspiWidth::NONE,
|
||||
instruction: 0,
|
||||
address: None,
|
||||
dummy: DummyCycles::_0,
|
||||
data_len: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
||||
/// If you need other value the whose predefined use `Other` variant.
|
||||
pub memory_size: MemorySize,
|
||||
/// Address size (8/16/24/32-bit)
|
||||
pub address_size: AddressSize,
|
||||
/// Scalar factor for generating CLK [0-255]
|
||||
pub prescaler: u8,
|
||||
/// Number of bytes to trigger FIFO threshold flag.
|
||||
pub fifo_threshold: FIFOThresholdLevel,
|
||||
/// Minimum number of cycles that chip select must be high between issued commands
|
||||
pub cs_high_time: ChipSelectHightTime,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
memory_size: MemorySize::Other(0),
|
||||
address_size: AddressSize::_24bit,
|
||||
prescaler: 128,
|
||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Qspi<'d, T: Instance, Dma> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
peri,
|
||||
Some(d0.map_into()),
|
||||
Some(d1.map_into()),
|
||||
Some(d2.map_into()),
|
||||
Some(d3.map_into()),
|
||||
Some(sck.map_into()),
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
|
||||
T::enable();
|
||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
T::REGS.cr().write(|w| {
|
||||
w.set_prescaler(config.prescaler);
|
||||
w.set_en(true);
|
||||
});
|
||||
T::REGS.dcr().write(|w| {
|
||||
w.set_fsize(config.memory_size.into());
|
||||
w.set_csht(config.cs_high_time.into());
|
||||
w.set_ckmode(false);
|
||||
});
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
sck,
|
||||
d0,
|
||||
d1,
|
||||
d2,
|
||||
d3,
|
||||
nss,
|
||||
dma,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(&mut self, transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
||||
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().ftf() {}
|
||||
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
buf,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_write(
|
||||
&mut self.dma,
|
||||
request,
|
||||
buf,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
||||
T::REGS.fcr().modify(|v| {
|
||||
v.set_csmf(true);
|
||||
v.set_ctcf(true);
|
||||
v.set_ctef(true);
|
||||
v.set_ctof(true);
|
||||
});
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
||||
}
|
||||
|
||||
T::REGS.ccr().write(|v| {
|
||||
v.set_fmode(fmode.into());
|
||||
v.set_imode(transaction.iwidth.into());
|
||||
v.set_instruction(transaction.instruction);
|
||||
v.set_admode(transaction.awidth.into());
|
||||
v.set_adsize(self.config.address_size.into());
|
||||
v.set_dmode(transaction.dwidth.into());
|
||||
v.set_abmode(QspiWidth::NONE.into());
|
||||
v.set_dcyc(transaction.dummy.into());
|
||||
});
|
||||
|
||||
if let Some(addr) = transaction.address {
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
const REGS: Regs;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
|
||||
pin_trait!(SckPin, Instance);
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(NSSPin, Instance);
|
||||
|
||||
dma_trait!(QuadDma, Instance);
|
||||
|
||||
foreach_peripheral!(
|
||||
(quadspi, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGS: Regs = crate::pac::$inst;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user