Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf13f70ea9 | |||
6bdacb4f69 | |||
ff3baf1e90 | |||
d5dcbadbbe | |||
0ad0d27150 | |||
03d500f548 | |||
8b46343b34 | |||
a3ea01473a | |||
cf84c8bfd1 | |||
09d7950313 | |||
267cbaebe6 | |||
31fc337e2f | |||
135f350020 | |||
897663e023 | |||
2218d30c80 | |||
f5c9e3baa6 | |||
47bac9df70 | |||
3efc3eee57 | |||
bc65b8f7ec | |||
78f8e6112a | |||
5528c33649 |
3
ci.sh
3
ci.sh
@ -190,6 +190,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --out-dir out/tests/stm32f446re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
||||||
@ -220,6 +221,8 @@ cargo batch \
|
|||||||
rm out/tests/stm32wb55rg/wpan_mac
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
rm out/tests/stm32wb55rg/wpan_ble
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
|
# not in CI yet.
|
||||||
|
rm -rf out/tests/stm32f446re
|
||||||
|
|
||||||
# unstable, I think it's running out of RAM?
|
# unstable, I think it's running out of RAM?
|
||||||
rm out/tests/stm32f207zg/eth
|
rm out/tests/stm32f207zg/eth
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
#![feature(async_fn_in_trait, type_alias_impl_trait)]
|
||||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
|
@ -1,54 +1,48 @@
|
|||||||
macro_rules! nvram {
|
pub static NVRAM: &'static [u8] = b"
|
||||||
($($s:literal,)*) => {
|
NVRAMRev=$Rev$\x00\
|
||||||
concat_bytes!($($s, b"\x00",)* b"\x00\x00")
|
manfid=0x2d0\x00\
|
||||||
};
|
prodid=0x0727\x00\
|
||||||
}
|
vendid=0x14e4\x00\
|
||||||
|
devid=0x43e2\x00\
|
||||||
pub static NVRAM: &'static [u8] = &*nvram!(
|
boardtype=0x0887\x00\
|
||||||
b"NVRAMRev=$Rev$",
|
boardrev=0x1100\x00\
|
||||||
b"manfid=0x2d0",
|
boardnum=22\x00\
|
||||||
b"prodid=0x0727",
|
macaddr=00:A0:50:b5:59:5e\x00\
|
||||||
b"vendid=0x14e4",
|
sromrev=11\x00\
|
||||||
b"devid=0x43e2",
|
boardflags=0x00404001\x00\
|
||||||
b"boardtype=0x0887",
|
boardflags3=0x04000000\x00\
|
||||||
b"boardrev=0x1100",
|
xtalfreq=37400\x00\
|
||||||
b"boardnum=22",
|
nocrc=1\x00\
|
||||||
b"macaddr=00:A0:50:b5:59:5e",
|
ag0=255\x00\
|
||||||
b"sromrev=11",
|
aa2g=1\x00\
|
||||||
b"boardflags=0x00404001",
|
ccode=ALL\x00\
|
||||||
b"boardflags3=0x04000000",
|
pa0itssit=0x20\x00\
|
||||||
b"xtalfreq=37400",
|
extpagain2g=0\x00\
|
||||||
b"nocrc=1",
|
pa2ga0=-168,6649,-778\x00\
|
||||||
b"ag0=255",
|
AvVmid_c0=0x0,0xc8\x00\
|
||||||
b"aa2g=1",
|
cckpwroffset0=5\x00\
|
||||||
b"ccode=ALL",
|
maxp2ga0=84\x00\
|
||||||
b"pa0itssit=0x20",
|
txpwrbckof=6\x00\
|
||||||
b"extpagain2g=0",
|
cckbw202gpo=0\x00\
|
||||||
b"pa2ga0=-168,6649,-778",
|
legofdmbw202gpo=0x66111111\x00\
|
||||||
b"AvVmid_c0=0x0,0xc8",
|
mcsbw202gpo=0x77711111\x00\
|
||||||
b"cckpwroffset0=5",
|
propbw202gpo=0xdd\x00\
|
||||||
b"maxp2ga0=84",
|
ofdmdigfilttype=18\x00\
|
||||||
b"txpwrbckof=6",
|
ofdmdigfilttypebe=18\x00\
|
||||||
b"cckbw202gpo=0",
|
papdmode=1\x00\
|
||||||
b"legofdmbw202gpo=0x66111111",
|
papdvalidtest=1\x00\
|
||||||
b"mcsbw202gpo=0x77711111",
|
pacalidx2g=45\x00\
|
||||||
b"propbw202gpo=0xdd",
|
papdepsoffset=-30\x00\
|
||||||
b"ofdmdigfilttype=18",
|
papdendidx=58\x00\
|
||||||
b"ofdmdigfilttypebe=18",
|
ltecxmux=0\x00\
|
||||||
b"papdmode=1",
|
ltecxpadnum=0x0102\x00\
|
||||||
b"papdvalidtest=1",
|
ltecxfnsel=0x44\x00\
|
||||||
b"pacalidx2g=45",
|
ltecxgcigpio=0x01\x00\
|
||||||
b"papdepsoffset=-30",
|
il0macaddr=00:90:4c:c5:12:38\x00\
|
||||||
b"papdendidx=58",
|
wl0id=0x431b\x00\
|
||||||
b"ltecxmux=0",
|
deadman_to=0xffffffff\x00\
|
||||||
b"ltecxpadnum=0x0102",
|
muxenab=0x100\x00\
|
||||||
b"ltecxfnsel=0x44",
|
spurconfig=0x3\x00\
|
||||||
b"ltecxgcigpio=0x01",
|
glitch_based_crsmin=1\x00\
|
||||||
b"il0macaddr=00:90:4c:c5:12:38",
|
btc_mode=1\x00\
|
||||||
b"wl0id=0x431b",
|
\x00";
|
||||||
b"deadman_to=0xffffffff",
|
|
||||||
b"muxenab=0x100",
|
|
||||||
b"spurconfig=0x3",
|
|
||||||
b"glitch_based_crsmin=1",
|
|
||||||
b"btc_mode=1",
|
|
||||||
);
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
@ -66,19 +66,29 @@ where
|
|||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
let op_res = 'ops: {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
match op {
|
let res = match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await?,
|
Operation::Read(buf) => bus.read(buf).await,
|
||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
Operation::DelayUs(us) => match bus.flush().await {
|
||||||
|
Err(e) => Err(e),
|
||||||
|
Ok(()) => {
|
||||||
|
embassy_time::Timer::after_micros(*us as _).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
break 'ops Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -131,19 +141,29 @@ where
|
|||||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
let op_res = 'ops: {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
match op {
|
let res = match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await?,
|
Operation::Read(buf) => bus.read(buf).await,
|
||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
Operation::DelayUs(us) => match bus.flush().await {
|
||||||
|
Err(e) => Err(e),
|
||||||
|
Ok(()) => {
|
||||||
|
embassy_time::Timer::after_micros(*us as _).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
break 'ops Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
any(feature = "ble", feature = "mac"),
|
any(feature = "ble", feature = "mac"),
|
||||||
allow(stable_features, unknown_lints, async_fn_in_trait)
|
allow(stable_features, unknown_lints, async_fn_in_trait)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
|
|
||||||
|
|
||||||
// This must go FIRST so that all the other modules see its macros.
|
// This must go FIRST so that all the other modules see its macros.
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
@ -58,7 +58,7 @@ rand_core = "0.6.3"
|
|||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" }
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -65,7 +65,6 @@ fn main() {
|
|||||||
match r.kind {
|
match r.kind {
|
||||||
// Generate singletons per pin, not per port
|
// Generate singletons per pin, not per port
|
||||||
"gpio" => {
|
"gpio" => {
|
||||||
println!("{}", p.name);
|
|
||||||
let port_letter = p.name.strip_prefix("GPIO").unwrap();
|
let port_letter = p.name.strip_prefix("GPIO").unwrap();
|
||||||
for pin_num in 0..16 {
|
for pin_num in 0..16 {
|
||||||
singletons.push(format!("P{}{}", port_letter, pin_num));
|
singletons.push(format!("P{}{}", port_letter, pin_num));
|
||||||
@ -997,8 +996,8 @@ fn main() {
|
|||||||
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
||||||
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
||||||
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
||||||
(("dac", "CH1"), quote!(crate::dac::DmaCh1)),
|
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
|
||||||
(("dac", "CH2"), quote!(crate::dac::DmaCh2)),
|
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
|
||||||
]
|
]
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
@ -1138,6 +1137,23 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========
|
||||||
|
// Write peripheral_interrupts module.
|
||||||
|
let mut mt = TokenStream::new();
|
||||||
|
for p in METADATA.peripherals {
|
||||||
|
let mut pt = TokenStream::new();
|
||||||
|
|
||||||
|
for irq in p.interrupts {
|
||||||
|
let iname = format_ident!("{}", irq.interrupt);
|
||||||
|
let sname = format_ident!("{}", irq.signal);
|
||||||
|
pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pname = format_ident!("{}", p.name);
|
||||||
|
mt.extend(quote!(pub mod #pname { #pt }));
|
||||||
|
}
|
||||||
|
g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));
|
||||||
|
|
||||||
// ========
|
// ========
|
||||||
// Write foreach_foo! macrotables
|
// Write foreach_foo! macrotables
|
||||||
|
|
||||||
@ -1296,6 +1312,9 @@ fn main() {
|
|||||||
|
|
||||||
let mut m = String::new();
|
let mut m = String::new();
|
||||||
|
|
||||||
|
// DO NOT ADD more macros like these.
|
||||||
|
// These turned to be a bad idea!
|
||||||
|
// Instead, make build.rs generate the final code.
|
||||||
make_table(&mut m, "foreach_flash_region", &flash_regions_table);
|
make_table(&mut m, "foreach_flash_region", &flash_regions_table);
|
||||||
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
||||||
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
||||||
@ -1332,15 +1351,6 @@ fn main() {
|
|||||||
|
|
||||||
if let Some(core) = core_name {
|
if let Some(core) = core_name {
|
||||||
println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core);
|
println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core);
|
||||||
} else {
|
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========
|
|
||||||
// stm32f3 wildcard features used in RCC
|
|
||||||
|
|
||||||
if chip_name.starts_with("stm32f3") {
|
|
||||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =======
|
// =======
|
||||||
@ -1355,16 +1365,25 @@ fn main() {
|
|||||||
if &chip_name[..8] == "stm32wba" {
|
if &chip_name[..8] == "stm32wba" {
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba
|
println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52
|
println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52
|
||||||
|
println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]);
|
||||||
|
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]);
|
||||||
} else {
|
} else {
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
||||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||||
|
println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]);
|
||||||
|
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle time-driver-XXXX features.
|
// Mark the L4+ chips as they have many differences to regular L4.
|
||||||
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
|
if &chip_name[..7] == "stm32l4" {
|
||||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
if "pqrs".contains(&chip_name[7..8]) {
|
||||||
|
println!("cargo:rustc-cfg=stm32l4_plus");
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-cfg=stm32l4_nonplus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
}
|
}
|
||||||
|
@ -1,136 +1,66 @@
|
|||||||
|
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
|
use crate::dma::NoDma;
|
||||||
|
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||||
use crate::pac::dac;
|
use crate::pac::dac;
|
||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
|
mod tsel;
|
||||||
|
pub use tsel::TriggerSel;
|
||||||
|
|
||||||
|
/// Operating mode for DAC channel
|
||||||
|
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// Custom Errors
|
pub enum Mode {
|
||||||
pub enum Error {
|
/// Normal mode, channel is connected to external pin with buffer enabled.
|
||||||
UnconfiguredChannel,
|
NormalExternalBuffered,
|
||||||
InvalidValue,
|
/// Normal mode, channel is connected to external pin and internal peripherals
|
||||||
|
/// with buffer enabled.
|
||||||
|
NormalBothBuffered,
|
||||||
|
/// Normal mode, channel is connected to external pin with buffer disabled.
|
||||||
|
NormalExternalUnbuffered,
|
||||||
|
/// Normal mode, channel is connected to internal peripherals with buffer disabled.
|
||||||
|
NormalInternalUnbuffered,
|
||||||
|
/// Sample-and-hold mode, channel is connected to external pin with buffer enabled.
|
||||||
|
SampleHoldExternalBuffered,
|
||||||
|
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||||
|
/// with buffer enabled.
|
||||||
|
SampleHoldBothBuffered,
|
||||||
|
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||||
|
/// with buffer disabled.
|
||||||
|
SampleHoldBothUnbuffered,
|
||||||
|
/// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled.
|
||||||
|
SampleHoldInternalUnbuffered,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
impl Mode {
|
||||||
/// DAC Channels
|
fn mode(&self) -> dac::vals::Mode {
|
||||||
pub enum Channel {
|
|
||||||
Ch1,
|
|
||||||
Ch2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
const fn index(&self) -> usize {
|
|
||||||
match self {
|
match self {
|
||||||
Channel::Ch1 => 0,
|
Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN,
|
||||||
Channel::Ch2 => 1,
|
Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN,
|
||||||
|
Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS,
|
||||||
|
Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS,
|
||||||
|
Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN,
|
||||||
|
Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN,
|
||||||
|
Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS,
|
||||||
|
Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// Trigger sources for CH1
|
/// Single 8 or 12 bit value that can be output by the DAC.
|
||||||
pub enum Ch1Trigger {
|
///
|
||||||
#[cfg(dac_v3)]
|
/// 12-bit values outside the permitted range are silently truncated.
|
||||||
Tim1,
|
|
||||||
Tim2,
|
|
||||||
#[cfg(not(dac_v3))]
|
|
||||||
Tim3,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Tim4,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Tim5,
|
|
||||||
Tim6,
|
|
||||||
Tim7,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Tim8,
|
|
||||||
Tim15,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Hrtim1Dactrg1,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Hrtim1Dactrg2,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Lptim1,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Lptim2,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Lptim3,
|
|
||||||
Exti9,
|
|
||||||
Software,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ch1Trigger {
|
|
||||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
|
||||||
match self {
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO,
|
|
||||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
|
||||||
#[cfg(not(dac_v3))]
|
|
||||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO,
|
|
||||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
|
||||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO,
|
|
||||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT,
|
|
||||||
#[cfg(dac_v3)]
|
|
||||||
Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT,
|
|
||||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
|
||||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
/// Trigger sources for CH2
|
|
||||||
pub enum Ch2Trigger {
|
|
||||||
Tim6,
|
|
||||||
Tim8,
|
|
||||||
Tim7,
|
|
||||||
Tim5,
|
|
||||||
Tim2,
|
|
||||||
Tim4,
|
|
||||||
Exti9,
|
|
||||||
Software,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ch2Trigger {
|
|
||||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
|
||||||
match self {
|
|
||||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
|
||||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
|
||||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
|
||||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
|
||||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
|
||||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
|
||||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
|
||||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
/// Single 8 or 12 bit value that can be output by the DAC
|
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
// 8 bit value
|
// 8 bit value
|
||||||
Bit8(u8),
|
Bit8(u8),
|
||||||
@ -142,7 +72,21 @@ pub enum Value {
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// Array variant of [`Value`]
|
/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.
|
||||||
|
///
|
||||||
|
/// 12-bit values outside the permitted range are silently truncated.
|
||||||
|
pub enum DualValue {
|
||||||
|
// 8 bit value
|
||||||
|
Bit8(u8, u8),
|
||||||
|
// 12 bit value stored in a u16, left-aligned
|
||||||
|
Bit12Left(u16, u16),
|
||||||
|
// 12 bit value stored in a u16, right-aligned
|
||||||
|
Bit12Right(u16, u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Array variant of [`Value`].
|
||||||
pub enum ValueArray<'a> {
|
pub enum ValueArray<'a> {
|
||||||
// 8 bit values
|
// 8 bit values
|
||||||
Bit8(&'a [u8]),
|
Bit8(&'a [u8]),
|
||||||
@ -151,398 +95,395 @@ pub enum ValueArray<'a> {
|
|||||||
// 12 bit values stored in a u16, right-aligned
|
// 12 bit values stored in a u16, right-aligned
|
||||||
Bit12Right(&'a [u16]),
|
Bit12Right(&'a [u16]),
|
||||||
}
|
}
|
||||||
/// Provide common functions for DAC channels
|
|
||||||
pub trait DacChannel<T: Instance, Tx> {
|
|
||||||
const CHANNEL: Channel;
|
|
||||||
|
|
||||||
/// Enable trigger of the given channel
|
/// Driver for a single DAC channel.
|
||||||
fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_ten(Self::CHANNEL.index(), on);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set mode register of the given channel
|
|
||||||
#[cfg(any(dac_v2, dac_v3))]
|
|
||||||
fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> {
|
|
||||||
T::regs().mcr().modify(|reg| {
|
|
||||||
reg.set_mode(Self::CHANNEL.index(), val);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set enable register of the given channel
|
|
||||||
fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_en(Self::CHANNEL.index(), on);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enable the DAC channel `ch`
|
|
||||||
fn enable_channel(&mut self) -> Result<(), Error> {
|
|
||||||
self.set_channel_enable(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable the DAC channel `ch`
|
|
||||||
fn disable_channel(&mut self) -> Result<(), Error> {
|
|
||||||
self.set_channel_enable(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a software trigger on `ch`
|
|
||||||
fn trigger(&mut self) {
|
|
||||||
T::regs().swtrigr().write(|reg| {
|
|
||||||
reg.set_swtrig(Self::CHANNEL.index(), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a value to be output by the DAC on trigger.
|
|
||||||
///
|
|
||||||
/// The `value` is written to the corresponding "data holding register".
|
|
||||||
fn set(&mut self, value: Value) -> Result<(), Error> {
|
|
||||||
match value {
|
|
||||||
Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hold two DAC channels
|
|
||||||
///
|
///
|
||||||
/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
|
/// If you want to use both channels, either together or independently,
|
||||||
///
|
/// create a [`Dac`] first and use it to access each channel.
|
||||||
/// # Example for obtaining both DAC channels
|
pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// // DMA channels and pins may need to be changed for your controller
|
|
||||||
/// let (dac_ch1, dac_ch2) =
|
|
||||||
/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
|
|
||||||
/// ```
|
|
||||||
pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
|
|
||||||
ch1: DacCh1<'d, T, TxCh1>,
|
|
||||||
ch2: DacCh2<'d, T, TxCh2>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DAC CH1
|
|
||||||
///
|
|
||||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
|
||||||
pub struct DacCh1<'d, T: Instance, Tx> {
|
|
||||||
/// To consume T
|
|
||||||
_peri: PeripheralRef<'d, T>,
|
|
||||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
|
||||||
dma: PeripheralRef<'d, Tx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DAC CH2
|
|
||||||
///
|
|
||||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
|
||||||
pub struct DacCh2<'d, T: Instance, Tx> {
|
|
||||||
/// Instead of PeripheralRef to consume T
|
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
#[allow(unused)]
|
||||||
dma: PeripheralRef<'d, Tx>,
|
dma: PeripheralRef<'d, DMA>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
|
pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
|
||||||
/// Obtain DAC CH1
|
pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
|
||||||
pub fn new(
|
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
|
||||||
dma: impl Peripheral<P = Tx> + 'd,
|
|
||||||
pin: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
|
|
||||||
) -> Self {
|
|
||||||
pin.set_as_analog();
|
|
||||||
into_ref!(peri, dma);
|
|
||||||
T::enable_and_reset();
|
|
||||||
|
|
||||||
let mut dac = Self { _peri: peri, dma };
|
impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
|
||||||
|
const IDX: usize = (N - 1) as usize;
|
||||||
|
|
||||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
/// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.
|
||||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
|
||||||
#[cfg(any(dac_v2, dac_v3))]
|
|
||||||
dac.set_channel_mode(0).unwrap();
|
|
||||||
dac.enable_channel().unwrap();
|
|
||||||
dac.set_trigger_enable(true).unwrap();
|
|
||||||
|
|
||||||
dac
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select a new trigger for this channel
|
|
||||||
///
|
///
|
||||||
/// **Important**: This disables the channel!
|
/// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
|
||||||
pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
|
||||||
unwrap!(self.disable_channel());
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_tsel1(trigger.tsel());
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write `data` to the DAC CH1 via DMA.
|
|
||||||
///
|
///
|
||||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
/// The channel is enabled on creation and begins to drive the output pin.
|
||||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will
|
||||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
/// disable the channel; you must re-enable it with `enable()`.
|
||||||
///
|
///
|
||||||
/// **Important:** Channel 1 has to be configured for the DAC instance!
|
/// By default, triggering is disabled, but it can be enabled using
|
||||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
/// [`DacChannel::set_trigger()`].
|
||||||
where
|
|
||||||
Tx: DmaCh1<T>,
|
|
||||||
{
|
|
||||||
let channel = Channel::Ch1.index();
|
|
||||||
debug!("Writing to channel {}", channel);
|
|
||||||
|
|
||||||
// Enable DAC and DMA
|
|
||||||
T::regs().cr().modify(|w| {
|
|
||||||
w.set_en(channel, true);
|
|
||||||
w.set_dmaen(channel, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
let tx_request = self.dma.request();
|
|
||||||
let dma_channel = &mut self.dma;
|
|
||||||
|
|
||||||
let tx_options = crate::dma::TransferOptions {
|
|
||||||
circular,
|
|
||||||
half_transfer_ir: false,
|
|
||||||
complete_transfer_ir: !circular,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
|
||||||
let tx_f = match data {
|
|
||||||
ValueArray::Bit8(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ValueArray::Bit12Left(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ValueArray::Bit12Right(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
tx_f.await;
|
|
||||||
|
|
||||||
// finish dma
|
|
||||||
// TODO: Do we need to check any status registers here?
|
|
||||||
T::regs().cr().modify(|w| {
|
|
||||||
// Disable the DAC peripheral
|
|
||||||
w.set_en(channel, false);
|
|
||||||
// Disable the DMA. TODO: Is this necessary?
|
|
||||||
w.set_dmaen(channel, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
|
|
||||||
/// Obtain DAC CH2
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
_peri: impl Peripheral<P = T> + 'd,
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Tx> + 'd,
|
dma: impl Peripheral<P = DMA> + 'd,
|
||||||
pin: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
|
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
into_ref!(dma, pin);
|
||||||
pin.set_as_analog();
|
pin.set_as_analog();
|
||||||
into_ref!(_peri, dma);
|
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
|
|
||||||
let mut dac = Self {
|
let mut dac = Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
dma,
|
dma,
|
||||||
};
|
};
|
||||||
|
#[cfg(any(dac_v5, dac_v6, dac_v7))]
|
||||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
dac.set_hfsel();
|
||||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
dac.enable();
|
||||||
#[cfg(any(dac_v2, dac_v3))]
|
|
||||||
dac.set_channel_mode(0).unwrap();
|
|
||||||
dac.enable_channel().unwrap();
|
|
||||||
dac.set_trigger_enable(true).unwrap();
|
|
||||||
|
|
||||||
dac
|
dac
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select a new trigger for this channel
|
/// Create a new `DacChannel` instance where the external output pin is not used,
|
||||||
pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
/// so the DAC can only be used to generate internal signals.
|
||||||
unwrap!(self.disable_channel());
|
/// The GPIO pin is therefore available to be used for other functions.
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_tsel2(trigger.tsel());
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write `data` to the DAC CH2 via DMA.
|
|
||||||
///
|
///
|
||||||
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
/// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
|
||||||
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
|
||||||
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
/// channel; you must re-enable it with `enable()`.
|
||||||
///
|
///
|
||||||
/// **Important:** Channel 2 has to be configured for the DAC instance!
|
/// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
|
||||||
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
Tx: DmaCh2<T>,
|
|
||||||
{
|
|
||||||
let channel = Channel::Ch2.index();
|
|
||||||
debug!("Writing to channel {}", channel);
|
|
||||||
|
|
||||||
// Enable DAC and DMA
|
|
||||||
T::regs().cr().modify(|w| {
|
|
||||||
w.set_en(channel, true);
|
|
||||||
w.set_dmaen(channel, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
let tx_request = self.dma.request();
|
|
||||||
let dma_channel = &mut self.dma;
|
|
||||||
|
|
||||||
let tx_options = crate::dma::TransferOptions {
|
|
||||||
circular,
|
|
||||||
half_transfer_ir: false,
|
|
||||||
complete_transfer_ir: !circular,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initiate the correct type of DMA transfer depending on what data is passed
|
|
||||||
let tx_f = match data {
|
|
||||||
ValueArray::Bit8(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ValueArray::Bit12Left(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ValueArray::Bit12Right(buf) => unsafe {
|
|
||||||
crate::dma::Transfer::new_write(
|
|
||||||
dma_channel,
|
|
||||||
tx_request,
|
|
||||||
buf,
|
|
||||||
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
|
||||||
tx_options,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
tx_f.await;
|
|
||||||
|
|
||||||
// finish dma
|
|
||||||
// TODO: Do we need to check any status registers here?
|
|
||||||
T::regs().cr().modify(|w| {
|
|
||||||
// Disable the DAC peripheral
|
|
||||||
w.set_en(channel, false);
|
|
||||||
// Disable the DMA. TODO: Is this necessary?
|
|
||||||
w.set_dmaen(channel, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
|
|
||||||
/// Create a new DAC instance with both channels.
|
|
||||||
///
|
///
|
||||||
/// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
|
/// By default, triggering is disabled, but it can be enabled using
|
||||||
pub fn new(
|
/// [`DacChannel::set_trigger()`].
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
#[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
|
||||||
dma_ch1: impl Peripheral<P = TxCh1> + 'd,
|
pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self {
|
||||||
dma_ch2: impl Peripheral<P = TxCh2> + 'd,
|
into_ref!(dma);
|
||||||
pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
|
|
||||||
pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
|
|
||||||
) -> Self {
|
|
||||||
pin_ch1.set_as_analog();
|
|
||||||
pin_ch2.set_as_analog();
|
|
||||||
into_ref!(peri, dma_ch1, dma_ch2);
|
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
|
let mut dac = Self {
|
||||||
let mut dac_ch1 = DacCh1 {
|
|
||||||
_peri: peri,
|
|
||||||
dma: dma_ch1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dac_ch2 = DacCh2 {
|
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
dma: dma_ch2,
|
dma,
|
||||||
};
|
};
|
||||||
|
#[cfg(any(dac_v5, dac_v6, dac_v7))]
|
||||||
|
dac.set_hfsel();
|
||||||
|
dac.set_mode(Mode::NormalInternalUnbuffered);
|
||||||
|
dac.enable();
|
||||||
|
dac
|
||||||
|
}
|
||||||
|
|
||||||
// Configure each activated channel. All results can be `unwrap`ed since they
|
/// Enable or disable this channel.
|
||||||
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
pub fn set_enable(&mut self, on: bool) {
|
||||||
#[cfg(any(dac_v2, dac_v3))]
|
critical_section::with(|_| {
|
||||||
dac_ch1.set_channel_mode(0).unwrap();
|
T::regs().cr().modify(|reg| {
|
||||||
dac_ch1.enable_channel().unwrap();
|
reg.set_en(Self::IDX, on);
|
||||||
dac_ch1.set_trigger_enable(true).unwrap();
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(dac_v2, dac_v3))]
|
/// Enable this channel.
|
||||||
dac_ch2.set_channel_mode(0).unwrap();
|
pub fn enable(&mut self) {
|
||||||
dac_ch2.enable_channel().unwrap();
|
self.set_enable(true)
|
||||||
dac_ch2.set_trigger_enable(true).unwrap();
|
}
|
||||||
|
|
||||||
Self {
|
/// Disable this channel.
|
||||||
ch1: dac_ch1,
|
pub fn disable(&mut self) {
|
||||||
ch2: dac_ch2,
|
self.set_enable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the trigger source for this channel.
|
||||||
|
///
|
||||||
|
/// This method disables the channel, so you may need to re-enable afterwards.
|
||||||
|
pub fn set_trigger(&mut self, source: TriggerSel) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_en(Self::IDX, false);
|
||||||
|
reg.set_tsel(Self::IDX, source as u8);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable or disable triggering for this channel.
|
||||||
|
pub fn set_triggering(&mut self, on: bool) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_ten(Self::IDX, on);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Software trigger this channel.
|
||||||
|
pub fn trigger(&mut self) {
|
||||||
|
T::regs().swtrigr().write(|reg| {
|
||||||
|
reg.set_swtrig(Self::IDX, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set mode of this channel.
|
||||||
|
///
|
||||||
|
/// This method disables the channel, so you may need to re-enable afterwards.
|
||||||
|
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||||
|
pub fn set_mode(&mut self, mode: Mode) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_en(Self::IDX, false);
|
||||||
|
});
|
||||||
|
T::regs().mcr().modify(|reg| {
|
||||||
|
reg.set_mode(Self::IDX, mode.mode());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a new value to this channel.
|
||||||
|
///
|
||||||
|
/// If triggering is not enabled, the new value is immediately output; otherwise,
|
||||||
|
/// it will be output after the next trigger.
|
||||||
|
pub fn set(&mut self, value: Value) {
|
||||||
|
match value {
|
||||||
|
Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||||
|
Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||||
|
Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split the DAC into CH1 and CH2 for independent use.
|
/// Read the current output value of the DAC.
|
||||||
pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
|
pub fn read(&self) -> u16 {
|
||||||
|
T::regs().dor(Self::IDX).read().dor()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set HFSEL as appropriate for the current peripheral clock frequency.
|
||||||
|
#[cfg(dac_v5)]
|
||||||
|
fn set_hfsel(&mut self) {
|
||||||
|
if T::frequency() >= crate::time::mhz(80) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_hfsel(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set HFSEL as appropriate for the current peripheral clock frequency.
|
||||||
|
#[cfg(any(dac_v6, dac_v7))]
|
||||||
|
fn set_hfsel(&mut self) {
|
||||||
|
if T::frequency() >= crate::time::mhz(160) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().mcr().modify(|reg| {
|
||||||
|
reg.set_hfsel(0b10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if T::frequency() >= crate::time::mhz(80) {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
T::regs().mcr().modify(|reg| {
|
||||||
|
reg.set_hfsel(0b01);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_dma_methods {
|
||||||
|
($n:literal, $trait:ident) => {
|
||||||
|
impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA>
|
||||||
|
where
|
||||||
|
DMA: $trait<T>,
|
||||||
|
{
|
||||||
|
/// Write `data` to this channel via DMA.
|
||||||
|
///
|
||||||
|
/// To prevent delays or glitches when outputing a periodic waveform, the `circular`
|
||||||
|
/// flag can be set. This configures a circular DMA transfer that continually outputs
|
||||||
|
/// `data`. Note that for performance reasons in circular mode the transfer-complete
|
||||||
|
/// interrupt is disabled.
|
||||||
|
#[cfg(not(gpdma))]
|
||||||
|
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) {
|
||||||
|
// Enable DAC and DMA
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
w.set_en(Self::IDX, true);
|
||||||
|
w.set_dmaen(Self::IDX, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let tx_request = self.dma.request();
|
||||||
|
let dma_channel = &mut self.dma;
|
||||||
|
|
||||||
|
let tx_options = crate::dma::TransferOptions {
|
||||||
|
circular,
|
||||||
|
half_transfer_ir: false,
|
||||||
|
complete_transfer_ir: !circular,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||||
|
let tx_f = match data {
|
||||||
|
ValueArray::Bit8(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Left(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Right(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tx_f.await;
|
||||||
|
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
w.set_en(Self::IDX, false);
|
||||||
|
w.set_dmaen(Self::IDX, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_dma_methods!(1, DacDma1);
|
||||||
|
impl_dma_methods!(2, DacDma2);
|
||||||
|
|
||||||
|
impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
T::disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DAC driver.
|
||||||
|
///
|
||||||
|
/// Use this struct when you want to use both channels, either together or independently.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // Pins may need to be changed for your specific device.
|
||||||
|
/// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC, NoDma, NoDma, p.PA4, p.PA5).split();
|
||||||
|
/// ```
|
||||||
|
pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> {
|
||||||
|
ch1: DacChannel<'d, T, 1, DMACh1>,
|
||||||
|
ch2: DacChannel<'d, T, 2, DMACh2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
|
||||||
|
/// Create a new `Dac` instance, consuming the underlying DAC peripheral.
|
||||||
|
///
|
||||||
|
/// This struct allows you to access both channels of the DAC, where available. You can either
|
||||||
|
/// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use
|
||||||
|
/// the two channels together.
|
||||||
|
///
|
||||||
|
/// The channels are enabled on creation and begins to drive their output pins.
|
||||||
|
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will
|
||||||
|
/// disable the channel; you must re-enable them with `enable()`.
|
||||||
|
///
|
||||||
|
/// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
|
||||||
|
/// method on the underlying channels.
|
||||||
|
pub fn new(
|
||||||
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
|
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
|
||||||
|
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
|
||||||
|
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd,
|
||||||
|
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
|
||||||
|
pin_ch1.set_as_analog();
|
||||||
|
pin_ch2.set_as_analog();
|
||||||
|
// Enable twice to increment the DAC refcount for each channel.
|
||||||
|
T::enable_and_reset();
|
||||||
|
T::enable_and_reset();
|
||||||
|
Self {
|
||||||
|
ch1: DacCh1 {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma: dma_ch1,
|
||||||
|
},
|
||||||
|
ch2: DacCh2 {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma: dma_ch2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `Dac` instance where the external output pins are not used,
|
||||||
|
/// so the DAC can only be used to generate internal signals but the GPIO
|
||||||
|
/// pins remain available for other functions.
|
||||||
|
///
|
||||||
|
/// This struct allows you to access both channels of the DAC, where available. You can either
|
||||||
|
/// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two
|
||||||
|
/// channels together.
|
||||||
|
///
|
||||||
|
/// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
|
||||||
|
/// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
|
||||||
|
/// channel; you must re-enable them with `enable()`.
|
||||||
|
///
|
||||||
|
/// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
|
||||||
|
/// method on the underlying channels.
|
||||||
|
#[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
|
||||||
|
pub fn new_internal(
|
||||||
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
|
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
|
||||||
|
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(dma_ch1, dma_ch2);
|
||||||
|
// Enable twice to increment the DAC refcount for each channel.
|
||||||
|
T::enable_and_reset();
|
||||||
|
T::enable_and_reset();
|
||||||
|
Self {
|
||||||
|
ch1: DacCh1 {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma: dma_ch1,
|
||||||
|
},
|
||||||
|
ch2: DacCh2 {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma: dma_ch2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split this `Dac` into separate channels.
|
||||||
|
///
|
||||||
|
/// You can access and move the channels around separately after splitting.
|
||||||
|
pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) {
|
||||||
(self.ch1, self.ch2)
|
(self.ch1, self.ch2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get mutable reference to CH1
|
/// Temporarily access channel 1.
|
||||||
pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
|
pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> {
|
||||||
&mut self.ch1
|
&mut self.ch1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get mutable reference to CH2
|
/// Temporarily access channel 2.
|
||||||
pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
|
pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> {
|
||||||
&mut self.ch2
|
&mut self.ch2
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get reference to CH1
|
/// Simultaneously update channels 1 and 2 with a new value.
|
||||||
pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
|
///
|
||||||
&self.ch1
|
/// If triggering is not enabled, the new values are immediately output;
|
||||||
|
/// otherwise, they will be output after the next trigger.
|
||||||
|
pub fn set(&mut self, values: DualValue) {
|
||||||
|
match values {
|
||||||
|
DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| {
|
||||||
|
reg.set_dhr(0, v1);
|
||||||
|
reg.set_dhr(1, v2);
|
||||||
|
}),
|
||||||
|
DualValue::Bit12Left(v1, v2) => T::regs().dhr12ld().write(|reg| {
|
||||||
|
reg.set_dhr(0, v1);
|
||||||
|
reg.set_dhr(1, v2);
|
||||||
|
}),
|
||||||
|
DualValue::Bit12Right(v1, v2) => T::regs().dhr12rd().write(|reg| {
|
||||||
|
reg.set_dhr(0, v1);
|
||||||
|
reg.set_dhr(1, v2);
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get reference to CH2
|
|
||||||
pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
|
|
||||||
&self.ch2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
|
|
||||||
const CHANNEL: Channel = Channel::Ch1;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
|
|
||||||
const CHANNEL: Channel = Channel::Ch2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
@ -552,34 +493,36 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||||
dma_trait!(DmaCh1, Instance);
|
dma_trait!(DacDma1, Instance);
|
||||||
dma_trait!(DmaCh2, Instance);
|
dma_trait!(DacDma2, Instance);
|
||||||
|
|
||||||
/// Marks a pin that can be used with the DAC
|
/// Marks a pin that can be used with the DAC
|
||||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||||
|
|
||||||
foreach_peripheral!(
|
foreach_peripheral!(
|
||||||
(dac, $inst:ident) => {
|
(dac, $inst:ident) => {
|
||||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
|
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
// TODO: Increment refcount?
|
||||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||||
}
|
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||||
|
}
|
||||||
|
|
||||||
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
// TODO: Decrement refcount?
|
||||||
}
|
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||||
|
|
||||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||||
fn regs() -> &'static crate::pac::dac::Dac {
|
fn regs() -> &'static crate::pac::dac::Dac {
|
||||||
|
282
embassy-stm32/src/dac/tsel.rs
Normal file
282
embassy-stm32/src/dac/tsel.rs
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/// Trigger selection for STM32F0.
|
||||||
|
#[cfg(stm32f0)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
Tim3 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
Tim15 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F1.
|
||||||
|
#[cfg(stm32f1)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
#[cfg(any(stm32f100, stm32f105, stm32f107))]
|
||||||
|
Tim3 = 1,
|
||||||
|
#[cfg(any(stm32f101, stm32f103))]
|
||||||
|
Tim8 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
#[cfg(any(stm32f101, stm32f103, stm32f105, stm32f107))]
|
||||||
|
Tim5 = 3,
|
||||||
|
#[cfg(all(stm32f100, any(flashsize_4, flashsize_6, flashsize_8, flashsize_b)))]
|
||||||
|
Tim15 = 3,
|
||||||
|
#[cfg(all(stm32f100, any(flashsize_c, flashsize_d, flashsize_e)))]
|
||||||
|
/// Can be remapped to TIM15 with MISC_REMAP in AFIO_MAPR2.
|
||||||
|
Tim5Or15 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F2/F4/F7/L4, except F410 or L4+.
|
||||||
|
#[cfg(all(any(stm32f2, stm32f4, stm32f7, stm32l4_nonplus), not(stm32f410)))]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
Tim8 = 1,
|
||||||
|
#[cfg(not(any(stm32l45x, stm32l46x)))]
|
||||||
|
Tim7 = 2,
|
||||||
|
Tim5 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F410.
|
||||||
|
#[cfg(stm32f410)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim5 = 3,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F301/2 and 318.
|
||||||
|
#[cfg(any(stm32f301, stm32f302, stm32f318))]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
#[cfg(stm32f302)]
|
||||||
|
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||||
|
Tim3 = 1,
|
||||||
|
Tim15 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
#[cfg(all(stm32f302, any(flashsize_6, flashsize_8)))]
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F303/3x8 (excluding 318 which is like 301, and 378 which is 37x).
|
||||||
|
#[cfg(any(stm32f303, stm32f328, stm32f358, stm32f398))]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
/// * DAC1: defaults to TIM8 but can be remapped to TIM3 with DAC_TRIG_RMP in SYSCFG_CFGR1
|
||||||
|
/// * DAC2: always TIM3
|
||||||
|
Tim8Or3 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
Tim15 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F37x.
|
||||||
|
#[cfg(any(stm32f373, stm32f378))]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
Tim3 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
/// TIM5 on DAC1, TIM18 on DAC2
|
||||||
|
Dac1Tim5Dac2Tim18 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32F334.
|
||||||
|
#[cfg(stm32f334)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||||
|
Tim3 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
/// Can be remapped to HRTIM_DACTRG1 using DAC1_TRIG3_RMP in SYSCFG_CFGR3.
|
||||||
|
Tim15OrHrtimDacTrg1 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
/// Requires DAC_TRIG5_RMP set in SYSCFG_CFGR3.
|
||||||
|
HrtimDacTrg2 = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32L0.
|
||||||
|
#[cfg(stm32l0)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
Tim3 = 1,
|
||||||
|
Tim3Ch3 = 2,
|
||||||
|
Tim21 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim7 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for STM32L1.
|
||||||
|
#[cfg(stm32l1)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Tim6 = 0,
|
||||||
|
Tim7 = 2,
|
||||||
|
Tim9 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Software = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for L4+, L5, U5, H7.
|
||||||
|
#[cfg(any(stm32l4_plus, stm32l5, stm32u5, stm32h7))]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Software = 0,
|
||||||
|
Tim1 = 1,
|
||||||
|
Tim2 = 2,
|
||||||
|
Tim4 = 3,
|
||||||
|
Tim5 = 4,
|
||||||
|
Tim6 = 5,
|
||||||
|
Tim7 = 6,
|
||||||
|
Tim8 = 7,
|
||||||
|
Tim15 = 8,
|
||||||
|
#[cfg(all(stm32h7, hrtim))]
|
||||||
|
Hrtim1DacTrg1 = 9,
|
||||||
|
#[cfg(all(stm32h7, hrtim))]
|
||||||
|
Hrtim1DacTrg2 = 10,
|
||||||
|
Lptim1 = 11,
|
||||||
|
#[cfg(not(stm32u5))]
|
||||||
|
Lptim2 = 12,
|
||||||
|
#[cfg(stm32u5)]
|
||||||
|
Lptim3 = 12,
|
||||||
|
Exti9 = 13,
|
||||||
|
#[cfg(any(stm32h7ax, stm32h7bx))]
|
||||||
|
/// RM0455 suggests this might be LPTIM2 on DAC1 and LPTIM3 on DAC2,
|
||||||
|
/// but it's probably wrong. Please let us know if you find out.
|
||||||
|
Lptim3 = 14,
|
||||||
|
#[cfg(any(stm32h72x, stm32h73x))]
|
||||||
|
Tim23 = 14,
|
||||||
|
#[cfg(any(stm32h72x, stm32h73x))]
|
||||||
|
Tim24 = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for H5.
|
||||||
|
#[cfg(stm32h5)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Software = 0,
|
||||||
|
Tim1 = 1,
|
||||||
|
Tim2 = 2,
|
||||||
|
#[cfg(any(stm32h56x, stm32h57x))]
|
||||||
|
Tim4 = 3,
|
||||||
|
#[cfg(stm32h503)]
|
||||||
|
Tim3 = 3,
|
||||||
|
#[cfg(any(stm32h56x, stm32h57x))]
|
||||||
|
Tim5 = 4,
|
||||||
|
Tim6 = 5,
|
||||||
|
Tim7 = 6,
|
||||||
|
#[cfg(any(stm32h56x, stm32h57x))]
|
||||||
|
Tim8 = 7,
|
||||||
|
#[cfg(any(stm32h56x, stm32h57x))]
|
||||||
|
Tim15 = 8,
|
||||||
|
Lptim1 = 11,
|
||||||
|
Lptim2 = 12,
|
||||||
|
Exti9 = 13,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for G0.
|
||||||
|
#[cfg(stm32g0)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Software = 0,
|
||||||
|
Tim1 = 1,
|
||||||
|
Tim2 = 2,
|
||||||
|
Tim3 = 3,
|
||||||
|
Tim6 = 5,
|
||||||
|
Tim7 = 6,
|
||||||
|
Tim15 = 8,
|
||||||
|
Lptim1 = 11,
|
||||||
|
Lptim2 = 12,
|
||||||
|
Exti9 = 13,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for G4.
|
||||||
|
#[cfg(stm32g4)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Software = 0,
|
||||||
|
/// * DAC1, DAC2, DAC4: TIM8
|
||||||
|
/// * DAC3: TIM1
|
||||||
|
Dac124Tim8Dac3Tim1 = 1,
|
||||||
|
Tim7 = 2,
|
||||||
|
Tim15 = 3,
|
||||||
|
Tim2 = 4,
|
||||||
|
Tim4 = 5,
|
||||||
|
Exti9 = 6,
|
||||||
|
Tim6 = 7,
|
||||||
|
Tim3 = 8,
|
||||||
|
HrtimDacRstTrg1 = 9,
|
||||||
|
HrtimDacRstTrg2 = 10,
|
||||||
|
HrtimDacRstTrg3 = 11,
|
||||||
|
HrtimDacRstTrg4 = 12,
|
||||||
|
HrtimDacRstTrg5 = 13,
|
||||||
|
HrtimDacRstTrg6 = 14,
|
||||||
|
/// * DAC1, DAC4: HRTIM_DAC_TRG1
|
||||||
|
/// * DAC2: HRTIM_DAC_TRG2
|
||||||
|
/// * DAC3: HRTIM_DAC_TRG3
|
||||||
|
HrtimDacTrg123 = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger selection for WL.
|
||||||
|
#[cfg(stm32wl)]
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum TriggerSel {
|
||||||
|
Software = 0,
|
||||||
|
Tim1 = 1,
|
||||||
|
Tim2 = 2,
|
||||||
|
Lptim1 = 11,
|
||||||
|
Lptim2 = 12,
|
||||||
|
Lptim3 = 13,
|
||||||
|
Exti9 = 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TriggerSel {
|
||||||
|
pub fn tsel(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
|
|
||||||
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
||||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
@ -23,6 +26,20 @@ pub enum Error {
|
|||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub waker: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
waker: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Instance: crate::rcc::RccPeripheral {
|
pub trait Instance: crate::rcc::RccPeripheral {
|
||||||
fn regs() -> crate::pac::i2c::I2c;
|
fn regs() -> crate::pac::i2c::I2c;
|
||||||
fn state() -> &'static State;
|
fn state() -> &'static State;
|
||||||
@ -30,7 +47,8 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + 'static {
|
pub trait Instance: sealed::Instance + 'static {
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type EventInterrupt: interrupt::typelevel::Interrupt;
|
||||||
|
type ErrorInterrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
pin_trait!(SclPin, Instance);
|
pin_trait!(SclPin, Instance);
|
||||||
@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance);
|
|||||||
dma_trait!(RxDma, Instance);
|
dma_trait!(RxDma, Instance);
|
||||||
dma_trait!(TxDma, Instance);
|
dma_trait!(TxDma, Instance);
|
||||||
|
|
||||||
foreach_interrupt!(
|
/// Interrupt handler.
|
||||||
($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
|
pub struct EventInterruptHandler<T: Instance> {
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
_version::on_interrupt::<T>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ErrorInterruptHandler<T: Instance> {
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
_version::on_interrupt::<T>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_peripheral!(
|
||||||
|
(i2c, $inst:ident) => {
|
||||||
impl sealed::Instance for peripherals::$inst {
|
impl sealed::Instance for peripherals::$inst {
|
||||||
fn regs() -> crate::pac::i2c::I2c {
|
fn regs() -> crate::pac::i2c::I2c {
|
||||||
crate::pac::$inst
|
crate::pac::$inst
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state() -> &'static State {
|
fn state() -> &'static sealed::State {
|
||||||
static STATE: State = State::new();
|
static STATE: sealed::State = sealed::State::new();
|
||||||
&STATE
|
&STATE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for peripherals::$inst {
|
impl Instance for peripherals::$inst {
|
||||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
|
||||||
|
type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mod eh02 {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_read(address, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_write(address, write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_write_read(address, write, read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-traits")]
|
||||||
|
mod eh1 {
|
||||||
|
use super::*;
|
||||||
|
use crate::dma::NoDma;
|
||||||
|
|
||||||
|
impl embedded_hal_1::i2c::Error for Error {
|
||||||
|
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
||||||
|
match *self {
|
||||||
|
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
|
||||||
|
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
||||||
|
Self::Nack => {
|
||||||
|
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
|
||||||
|
}
|
||||||
|
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
|
||||||
|
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
|
||||||
|
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
|
||||||
|
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
|
||||||
|
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_read(address, read)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_write(address, write)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_write_read(address, write, read)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
_address: u8,
|
||||||
|
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||||
|
mod eha {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
|
||||||
|
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.read(address, read).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.write(address, write).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.write_read(address, write, read).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: u8,
|
||||||
|
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let _ = address;
|
||||||
|
let _ = operations;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,23 +1,33 @@
|
|||||||
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::dma::NoDma;
|
use super::*;
|
||||||
|
use crate::dma::{NoDma, Transfer};
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
/// Interrupt handler.
|
pub unsafe fn on_interrupt<T: Instance>() {
|
||||||
pub struct InterruptHandler<T: Instance> {
|
let regs = T::regs();
|
||||||
_phantom: PhantomData<T>,
|
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
|
||||||
}
|
// other stuff, so we wake the task on every interrupt.
|
||||||
|
T::state().waker.wake();
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
critical_section::with(|_| {
|
||||||
unsafe fn on_interrupt() {}
|
// Clear event interrupt flag.
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_itevten(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -27,14 +37,6 @@ pub struct Config {
|
|||||||
pub scl_pullup: bool,
|
pub scl_pullup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub(crate) const fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -48,7 +50,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
_peri: impl Peripheral<P = T> + 'd,
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||||||
|
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||||||
|
+ 'd,
|
||||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
@ -98,6 +102,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
reg.set_pe(true);
|
reg.set_pe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unsafe { T::EventInterrupt::enable() };
|
||||||
|
unsafe { T::ErrorInterrupt::enable() };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
tx_dma,
|
tx_dma,
|
||||||
@ -105,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
|
||||||
// Note that flags should only be cleared once they have been registered. If flags are
|
// Note that flags should only be cleared once they have been registered. If flags are
|
||||||
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||||||
let sr1 = T::regs().sr1().read();
|
let sr1 = T::regs().sr1().read();
|
||||||
|
|
||||||
if sr1.timeout() {
|
if sr1.timeout() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_timeout(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_timeout(false);
|
||||||
|
});
|
||||||
return Err(Error::Timeout);
|
return Err(Error::Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.pecerr() {
|
if sr1.pecerr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_pecerr(false);
|
||||||
|
});
|
||||||
return Err(Error::Crc);
|
return Err(Error::Crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.ovr() {
|
if sr1.ovr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_ovr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_ovr(false);
|
||||||
|
});
|
||||||
return Err(Error::Overrun);
|
return Err(Error::Overrun);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.af() {
|
if sr1.af() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_af(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_af(false);
|
||||||
|
});
|
||||||
return Err(Error::Nack);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.arlo() {
|
if sr1.arlo() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_arlo(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_arlo(false);
|
||||||
|
});
|
||||||
return Err(Error::Arbitration);
|
return Err(Error::Arbitration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||||||
// clearing the BERR bit instead.
|
// clearing the BERR bit instead.
|
||||||
if sr1.berr() {
|
if sr1.berr() {
|
||||||
T::regs().sr1().modify(|reg| reg.set_berr(false));
|
T::regs().sr1().write(|reg| {
|
||||||
|
reg.0 = !0;
|
||||||
|
reg.set_berr(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(sr1)
|
Ok(sr1)
|
||||||
@ -157,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait until START condition was generated
|
// Wait until START condition was generated
|
||||||
while !self.check_and_clear_error_flags()?.start() {
|
while !Self::check_and_clear_error_flags()?.start() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also wait until signalled we're master and everything is waiting for us
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
while {
|
while {
|
||||||
self.check_and_clear_error_flags()?;
|
Self::check_and_clear_error_flags()?;
|
||||||
|
|
||||||
let sr2 = T::regs().sr2().read();
|
let sr2 = T::regs().sr2().read();
|
||||||
!sr2.msl() && !sr2.busy()
|
!sr2.msl() && !sr2.busy()
|
||||||
@ -177,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until address was sent
|
// Wait until address was sent
|
||||||
// Wait for the address to be acknowledged
|
// Wait for the address to be acknowledged
|
||||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||||
while !self.check_and_clear_error_flags()?.addr() {
|
while !Self::check_and_clear_error_flags()?.addr() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until we're ready for sending
|
// Wait until we're ready for sending
|
||||||
while {
|
while {
|
||||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||||
!self.check_and_clear_error_flags()?.txe()
|
!Self::check_and_clear_error_flags()?.txe()
|
||||||
} {
|
} {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
@ -208,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until byte is transferred
|
// Wait until byte is transferred
|
||||||
while {
|
while {
|
||||||
// Check for any potential error conditions.
|
// Check for any potential error conditions.
|
||||||
!self.check_and_clear_error_flags()?.btf()
|
!Self::check_and_clear_error_flags()?.btf()
|
||||||
} {
|
} {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
@ -219,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
||||||
while {
|
while {
|
||||||
// Check for any potential error conditions.
|
// Check for any potential error conditions.
|
||||||
self.check_and_clear_error_flags()?;
|
Self::check_and_clear_error_flags()?;
|
||||||
|
|
||||||
!T::regs().sr1().read().rxne()
|
!T::regs().sr1().read().rxne()
|
||||||
} {
|
} {
|
||||||
@ -244,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait until START condition was generated
|
// Wait until START condition was generated
|
||||||
while !self.check_and_clear_error_flags()?.start() {
|
while !Self::check_and_clear_error_flags()?.start() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
|
|
||||||
// Wait until address was sent
|
// Wait until address was sent
|
||||||
// Wait for the address to be acknowledged
|
// Wait for the address to be acknowledged
|
||||||
while !self.check_and_clear_error_flags()?.addr() {
|
while !Self::check_and_clear_error_flags()?.addr() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,6 +361,356 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||||||
self.blocking_write_read_timeout(addr, write, read, || Ok(()))
|
self.blocking_write_read_timeout(addr, write, read, || Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Async
|
||||||
|
|
||||||
|
#[inline] // pretty sure this should always be inlined
|
||||||
|
fn enable_interrupts() -> () {
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_iterren(true);
|
||||||
|
w.set_itevten(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
let dma_transfer = unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||||
|
w.set_dmaen(true);
|
||||||
|
w.set_itbufen(false);
|
||||||
|
});
|
||||||
|
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||||
|
let dst = regs.dr().as_ptr() as *mut u8;
|
||||||
|
|
||||||
|
let ch = &mut self.tx_dma;
|
||||||
|
let request = ch.request();
|
||||||
|
Transfer::new_write(ch, request, write, dst, Default::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drop = OnDrop::new(|| {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
w.set_itevten(false);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
|
||||||
|
// Send a START condition
|
||||||
|
T::regs().cr1().modify(|reg| {
|
||||||
|
reg.set_start(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let state = T::state();
|
||||||
|
|
||||||
|
// Wait until START condition was generated
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.start() {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(_) => {
|
||||||
|
let sr2 = T::regs().sr2().read();
|
||||||
|
if !sr2.msl() && !sr2.busy() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Set up current address, we're trying to talk to
|
||||||
|
Self::enable_interrupts();
|
||||||
|
T::regs().dr().write(|reg| reg.set_dr(address << 1));
|
||||||
|
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.addr() {
|
||||||
|
// Clear the ADDR condition by reading SR2.
|
||||||
|
T::regs().sr2().read();
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Self::enable_interrupts();
|
||||||
|
let poll_error = poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
// Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other
|
||||||
|
// identical poll_fn check_and_clear matches.
|
||||||
|
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||||
|
Ok(_) => Poll::Pending,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for either the DMA transfer to successfully finish, or an I2C error to occur.
|
||||||
|
match select(dma_transfer, poll_error).await {
|
||||||
|
Either::Second(Err(e)) => Err(e),
|
||||||
|
_ => Ok(()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
|
||||||
|
|
||||||
|
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
|
||||||
|
// requests then wait for a BTF event before programming the Stop condition.”
|
||||||
|
|
||||||
|
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.btf() {
|
||||||
|
if send_stop {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_stop(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
drop(on_drop);
|
||||||
|
|
||||||
|
// Fallthrough is success
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
self.write_with_stop(address, write, true).await?;
|
||||||
|
|
||||||
|
// Wait for STOP condition to transmit.
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
T::state().waker.register(cx.waker());
|
||||||
|
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||||
|
if T::regs().cr1().read().stop() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
|
{
|
||||||
|
let state = T::state();
|
||||||
|
let buffer_len = buffer.len();
|
||||||
|
|
||||||
|
let dma_transfer = unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||||
|
w.set_itbufen(false);
|
||||||
|
w.set_dmaen(true);
|
||||||
|
});
|
||||||
|
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||||
|
let src = regs.dr().as_ptr() as *mut u8;
|
||||||
|
|
||||||
|
let ch = &mut self.rx_dma;
|
||||||
|
let request = ch.request();
|
||||||
|
Transfer::new_read(ch, request, src, buffer, Default::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drop = OnDrop::new(|| {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_dmaen(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
w.set_itevten(false);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
|
||||||
|
// Send a START condition and set ACK bit
|
||||||
|
T::regs().cr1().modify(|reg| {
|
||||||
|
reg.set_start(true);
|
||||||
|
reg.set_ack(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait until START condition was generated
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.start() {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
// blocking read didn’t have a check_and_clear call here, but blocking write did so
|
||||||
|
// I’m adding it here in case that was an oversight.
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(_) => {
|
||||||
|
let sr2 = T::regs().sr2().read();
|
||||||
|
if !sr2.msl() && !sr2.busy() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Set up current address, we're trying to talk to
|
||||||
|
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
|
||||||
|
|
||||||
|
// Wait for the address to be acknowledged
|
||||||
|
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err(e)),
|
||||||
|
Ok(sr1) => {
|
||||||
|
if sr1.addr() {
|
||||||
|
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
|
||||||
|
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
|
||||||
|
if buffer_len == 1 {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_ack(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Clear ADDR condition by reading SR2
|
||||||
|
T::regs().sr2().read();
|
||||||
|
|
||||||
|
// 18.3.8: When a single byte must be received: [snip] Then the
|
||||||
|
// user can program the STOP condition either after clearing ADDR flag, or in the
|
||||||
|
// DMA Transfer Complete interrupt routine.
|
||||||
|
if buffer_len == 1 {
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_stop(true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If, in the I2C_CR2 register, the LAST bit is set, I2C
|
||||||
|
// automatically sends a NACK after the next byte following EOT_1. The user can
|
||||||
|
// generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
|
||||||
|
T::regs().cr2().modify(|w| {
|
||||||
|
w.set_last(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for bytes to be received, or an error to occur.
|
||||||
|
Self::enable_interrupts();
|
||||||
|
let poll_error = poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
|
||||||
|
match Self::check_and_clear_error_flags() {
|
||||||
|
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||||
|
_ => Poll::Pending,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match select(dma_transfer, poll_error).await {
|
||||||
|
Either::Second(Err(e)) => Err(e),
|
||||||
|
_ => Ok(()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// Wait for the STOP to be sent (STOP bit cleared).
|
||||||
|
Self::enable_interrupts();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||||
|
if T::regs().cr1().read().stop() {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
drop(on_drop);
|
||||||
|
|
||||||
|
// Fallthrough is success
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
self.write_with_stop(address, write, false).await?;
|
||||||
|
self.read(address, read).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||||||
@ -344,77 +719,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_read(addr, read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write(addr, write)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write_read(addr, write, read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-traits")]
|
|
||||||
mod eh1 {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl embedded_hal_1::i2c::Error for Error {
|
|
||||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
|
||||||
match *self {
|
|
||||||
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
|
|
||||||
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
|
||||||
Self::Nack => {
|
|
||||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
|
|
||||||
}
|
|
||||||
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
|
|
||||||
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
|
|
||||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_read(address, read)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write(address, write)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write_read(address, write, read)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction(
|
|
||||||
&mut self,
|
|
||||||
_address: u8,
|
|
||||||
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
Fast,
|
Fast,
|
||||||
Standard,
|
Standard,
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::dma::{NoDma, Transfer};
|
use crate::dma::{NoDma, Transfer};
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -36,25 +34,18 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
|
|||||||
move || Ok(())
|
move || Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler.
|
pub unsafe fn on_interrupt<T: Instance>() {
|
||||||
pub struct InterruptHandler<T: Instance> {
|
let regs = T::regs();
|
||||||
_phantom: PhantomData<T>,
|
let isr = regs.isr().read();
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
if isr.tcr() || isr.tc() {
|
||||||
unsafe fn on_interrupt() {
|
T::state().waker.wake();
|
||||||
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
|
|
||||||
critical_section::with(|_| {
|
|
||||||
regs.cr1().modify(|w| w.set_tcie(false));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// 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));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -77,18 +68,6 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
|
||||||
waker: AtomicWaker,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub(crate) const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
waker: AtomicWaker::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -104,7 +83,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||||||
|
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||||||
|
+ 'd,
|
||||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
@ -150,8 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
reg.set_pe(true);
|
reg.set_pe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
T::Interrupt::unpend();
|
unsafe { T::EventInterrupt::enable() };
|
||||||
unsafe { T::Interrupt::enable() };
|
unsafe { T::ErrorInterrupt::enable() };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
_peri: peri,
|
_peri: peri,
|
||||||
@ -987,35 +968,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
mod eh02 {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_read(address, buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write(address, write)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write_read(address, write, read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// I2C Stop Configuration
|
/// I2C Stop Configuration
|
||||||
///
|
///
|
||||||
/// Peripheral options for generating the STOP condition
|
/// Peripheral options for generating the STOP condition
|
||||||
@ -1140,83 +1092,6 @@ impl Timings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-traits")]
|
|
||||||
mod eh1 {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl embedded_hal_1::i2c::Error for Error {
|
|
||||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
|
||||||
match *self {
|
|
||||||
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
|
|
||||||
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
|
||||||
Self::Nack => {
|
|
||||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
|
|
||||||
}
|
|
||||||
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
|
|
||||||
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
|
|
||||||
type Error = Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
|
|
||||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_read(address, read)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write(address, write)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.blocking_write_read(address, write, read)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction(
|
|
||||||
&mut self,
|
|
||||||
_address: u8,
|
|
||||||
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
|
||||||
mod eha {
|
|
||||||
use super::super::{RxDma, TxDma};
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
|
|
||||||
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.read(address, read).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.write(address, write).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.write_read(address, write, read).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn transaction(
|
|
||||||
&mut self,
|
|
||||||
address: u8,
|
|
||||||
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let _ = address;
|
|
||||||
let _ = operations;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||||
type Config = Hertz;
|
type Config = Hertz;
|
||||||
type ConfigError = ();
|
type ConfigError = ();
|
||||||
|
@ -346,10 +346,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
|||||||
None => {
|
None => {
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
// For some chips PREDIV is always two, and cannot be changed
|
// For some chips PREDIV is always two, and cannot be changed
|
||||||
if #[cfg(any(
|
if #[cfg(any(flashsize_d, flashsize_e))] {
|
||||||
stm32f302xd, stm32f302xe, stm32f303xd,
|
|
||||||
stm32f303xe, stm32f398xe
|
|
||||||
))] {
|
|
||||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
|
let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
|
||||||
(
|
(
|
||||||
Hertz((HSI_FREQ.0 / divisor) * multiplier),
|
Hertz((HSI_FREQ.0 / divisor) * multiplier),
|
||||||
|
@ -1457,8 +1457,8 @@ cfg_if::cfg_if! {
|
|||||||
macro_rules! kernel_clk {
|
macro_rules! kernel_clk {
|
||||||
($inst:ident) => {
|
($inst:ident) => {
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
crate::rcc::get_freqs().pll1_q
|
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||||
}).expect("PLL48 is required for SDIO")
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if #[cfg(stm32f7)] {
|
} else if #[cfg(stm32f7)] {
|
||||||
@ -1469,7 +1469,7 @@ cfg_if::cfg_if! {
|
|||||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||||
crate::rcc::get_freqs().sys
|
crate::rcc::get_freqs().sys
|
||||||
} else {
|
} else {
|
||||||
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -1479,7 +1479,7 @@ cfg_if::cfg_if! {
|
|||||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||||
crate::rcc::get_freqs().sys
|
crate::rcc::get_freqs().sys
|
||||||
} else {
|
} else {
|
||||||
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can im
|
|||||||
traits to add support for using `embassy-usb` for a given chip/platform.
|
traits to add support for using `embassy-usb` for a given chip/platform.
|
||||||
|
|
||||||
The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
|
The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
|
||||||
APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used
|
APIs don't cause a semver-major bump of this crate. This allows existing HALs/BSPs to be used
|
||||||
with the newer `embassy-usb` without needing updates.
|
with the newer `embassy-usb` without needing updates.
|
||||||
|
|
||||||
If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
|
If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
use embassy_stm32::dac::{DacCh1, Value};
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
info!("Hello World, dude!");
|
info!("Hello World, dude!");
|
||||||
|
|
||||||
let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
|
let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
|
||||||
unwrap!(dac.set_trigger_enable(false));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1
|
||||||
|
// DMA peripherals changed to compile for stm32f429zi, for the CI.
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::i2c::I2c;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 96;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Hello world!");
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let mut i2c = I2c::new(
|
||||||
|
p.I2C1,
|
||||||
|
p.PB8,
|
||||||
|
p.PB7,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH6,
|
||||||
|
p.DMA1_CH0,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let a1454_read_sensor_command = [0x1F];
|
||||||
|
let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
|
||||||
|
match i2c
|
||||||
|
.write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
// Convert 12-bit signed integer into 16-bit signed integer.
|
||||||
|
// Is the 12 bit number negative?
|
||||||
|
if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 {
|
||||||
|
sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sensor_value_raw: u16 = sensor_data_buffer[3].into();
|
||||||
|
sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8;
|
||||||
|
let sensor_value: u16 = sensor_value_raw.into();
|
||||||
|
let sensor_value = sensor_value as i16;
|
||||||
|
info!("Data: {}", sensor_value);
|
||||||
|
}
|
||||||
|
Err(e) => error!("I2C Error during read: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3
|
||||||
|
// on the pins referenced in the peripheral definitions.
|
||||||
|
// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI.
|
||||||
|
// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x
|
||||||
|
// as long to complete as the blocking ones!
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::i2c::I2c;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use futures::future::try_join3;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 96;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||||
|
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer.
|
||||||
|
fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 {
|
||||||
|
let lower = buffer[3];
|
||||||
|
let mut upper = buffer[2];
|
||||||
|
// Fill in additional 1s if the 12 bit number is negative.
|
||||||
|
if (upper & 0b00001000) == 0b0001000 {
|
||||||
|
upper = upper | 0b11110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sensor_value_raw: u16 = lower.into();
|
||||||
|
sensor_value_raw |= (upper as u16) << 8;
|
||||||
|
let sensor_value: u16 = sensor_value_raw.into();
|
||||||
|
let sensor_value = sensor_value as i16;
|
||||||
|
sensor_value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Setting up peripherals.");
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let mut i2c1 = I2c::new(
|
||||||
|
p.I2C1,
|
||||||
|
p.PB8,
|
||||||
|
p.PB7,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH6,
|
||||||
|
p.DMA1_CH0,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i2c2 = I2c::new(
|
||||||
|
p.I2C2,
|
||||||
|
p.PB10,
|
||||||
|
p.PB11,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH7,
|
||||||
|
p.DMA1_CH3,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i2c3 = I2c::new(
|
||||||
|
p.I2C3,
|
||||||
|
p.PA8,
|
||||||
|
p.PC9,
|
||||||
|
Irqs,
|
||||||
|
p.DMA1_CH4,
|
||||||
|
p.DMA1_CH2,
|
||||||
|
Hertz(100_000),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let a1454_read_sensor_command = [0x1F];
|
||||||
|
let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||||
|
loop {
|
||||||
|
// Blocking reads one after the other. Completes in about 2000us.
|
||||||
|
let blocking_read_start_us = Instant::now().as_micros();
|
||||||
|
match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => error!("I2C Error: {:?}", e),
|
||||||
|
}
|
||||||
|
let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us;
|
||||||
|
info!(
|
||||||
|
"Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||||
|
blocking_read_total_us,
|
||||||
|
a1454_buf_to_i16(&i2c1_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c2_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c3_buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Async reads overlapping. Completes in about 1000us.
|
||||||
|
let async_read_start_us = Instant::now().as_micros();
|
||||||
|
|
||||||
|
let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer);
|
||||||
|
let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer);
|
||||||
|
let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer);
|
||||||
|
|
||||||
|
// Wait for all three transactions to finish, or any one of them to fail.
|
||||||
|
match try_join3(i2c1_result, i2c2_result, i2c3_result).await {
|
||||||
|
Ok(_) => {
|
||||||
|
let async_read_total_us = Instant::now().as_micros() - async_read_start_us;
|
||||||
|
info!(
|
||||||
|
"Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||||
|
async_read_total_us,
|
||||||
|
a1454_buf_to_i16(&i2c1_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c2_buffer),
|
||||||
|
a1454_buf_to_i16(&i2c3_buffer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => error!("I2C Error during async write-read: {}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
@ -19,7 +19,8 @@ const HEIGHT: usize = 100;
|
|||||||
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>;
|
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||||
|
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||||
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
|
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
use embassy_stm32::dac::{DacCh1, Value};
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -46,11 +46,10 @@ fn main() -> ! {
|
|||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||||
unwrap!(dac.set_trigger_enable(false));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,15 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||||
use micromath::F32Ext;
|
use micromath::F32Ext;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
pub type Dac1Type =
|
|
||||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
|
||||||
|
|
||||||
pub type Dac2Type =
|
|
||||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
@ -63,7 +57,7 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn dac_task1(mut dac: Dac1Type) {
|
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||||
let data: &[u8; 256] = &calculate_array::<256>();
|
let data: &[u8; 256] = &calculate_array::<256>();
|
||||||
|
|
||||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||||
@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
|||||||
error!("Reload value {} below threshold!", reload);
|
error!("Reload value {} below threshold!", reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||||
dac.enable_channel().unwrap();
|
dac.set_triggering(true);
|
||||||
|
dac.enable();
|
||||||
|
|
||||||
TIM6::enable_and_reset();
|
TIM6::enable_and_reset();
|
||||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
@ -100,14 +95,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
|||||||
// Loop technically not necessary if DMA circular mode is enabled
|
// Loop technically not necessary if DMA circular mode is enabled
|
||||||
loop {
|
loop {
|
||||||
info!("Loop DAC1");
|
info!("Loop DAC1");
|
||||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
dac.write(ValueArray::Bit8(data), true).await;
|
||||||
error!("Could not write to dac: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn dac_task2(mut dac: Dac2Type) {
|
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||||
let data: &[u8; 256] = &calculate_array::<256>();
|
let data: &[u8; 256] = &calculate_array::<256>();
|
||||||
|
|
||||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||||
@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
|||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||||
|
dac.set_triggering(true);
|
||||||
|
dac.enable();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||||
@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
|||||||
data.len()
|
data.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
dac.write(ValueArray::Bit8(data), true).await;
|
||||||
error!("Could not write to dac: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_sine_wave(v: u8) -> u8 {
|
fn to_sine_wave(v: u8) -> u8 {
|
||||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
use embassy_stm32::dac::{DacCh1, Value};
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -13,11 +13,10 @@ fn main() -> ! {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||||
unwrap!(dac.set_trigger_enable(false));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,15 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||||
use micromath::F32Ext;
|
use micromath::F32Ext;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
pub type Dac1Type =
|
|
||||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
|
||||||
|
|
||||||
pub type Dac2Type =
|
|
||||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let config = embassy_stm32::Config::default();
|
let config = embassy_stm32::Config::default();
|
||||||
@ -34,7 +28,7 @@ async fn main(spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn dac_task1(mut dac: Dac1Type) {
|
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||||
let data: &[u8; 256] = &calculate_array::<256>();
|
let data: &[u8; 256] = &calculate_array::<256>();
|
||||||
|
|
||||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||||
@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
|||||||
error!("Reload value {} below threshold!", reload);
|
error!("Reload value {} below threshold!", reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||||
dac.enable_channel().unwrap();
|
dac.set_triggering(true);
|
||||||
|
dac.enable();
|
||||||
|
|
||||||
TIM6::enable_and_reset();
|
TIM6::enable_and_reset();
|
||||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
@ -71,14 +66,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
|||||||
// Loop technically not necessary if DMA circular mode is enabled
|
// Loop technically not necessary if DMA circular mode is enabled
|
||||||
loop {
|
loop {
|
||||||
info!("Loop DAC1");
|
info!("Loop DAC1");
|
||||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
dac.write(ValueArray::Bit8(data), true).await;
|
||||||
error!("Could not write to dac: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn dac_task2(mut dac: Dac2Type) {
|
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||||
let data: &[u8; 256] = &calculate_array::<256>();
|
let data: &[u8; 256] = &calculate_array::<256>();
|
||||||
|
|
||||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||||
@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
|||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||||
|
dac.set_triggering(true);
|
||||||
|
dac.enable();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||||
@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
|||||||
data.len()
|
data.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
dac.write(ValueArray::Bit8(data), true).await;
|
||||||
error!("Could not write to dac: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_sine_wave(v: u8) -> u8 {
|
fn to_sine_wave(v: u8) -> u8 {
|
||||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||||
|
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||||
});
|
});
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
|
@ -40,7 +40,8 @@ use static_cell::make_static;
|
|||||||
use {embassy_stm32 as hal, panic_probe as _};
|
use {embassy_stm32 as hal, panic_probe as _};
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
|
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||||
|
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,28 +6,29 @@ license = "MIT OR Apache-2.0"
|
|||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]
|
|
||||||
stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"]
|
|
||||||
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"]
|
|
||||||
stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
|
stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
|
||||||
|
stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]
|
||||||
|
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
|
||||||
|
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
|
||||||
|
stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"]
|
||||||
|
stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin", "sdmmc"]
|
||||||
|
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
|
||||||
|
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"]
|
||||||
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"]
|
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"]
|
||||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"]
|
|
||||||
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"]
|
|
||||||
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]
|
|
||||||
stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
|
|
||||||
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
|
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
|
||||||
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"]
|
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"]
|
||||||
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
|
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"]
|
||||||
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"]
|
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]
|
||||||
stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
|
stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
|
||||||
stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
|
stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
|
||||||
|
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
|
||||||
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"]
|
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"]
|
||||||
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
|
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
|
||||||
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"]
|
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"]
|
||||||
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
|
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"]
|
||||||
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
|
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
|
||||||
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
|
stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
|
||||||
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
|
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"]
|
||||||
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
||||||
|
|
||||||
eth = []
|
eth = []
|
||||||
|
@ -27,21 +27,21 @@ bind_interrupts!(struct Irqs {
|
|||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut p = embassy_stm32::init(config());
|
let p = embassy_stm32::init(config());
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
// HW is connected as follows:
|
let can = peri!(p, CAN);
|
||||||
// PB13 -> PD0
|
let tx = peri!(p, CAN_TX);
|
||||||
// PB12 -> PD1
|
let mut rx = peri!(p, CAN_RX);
|
||||||
|
|
||||||
// The next two lines are a workaround for testing without transceiver.
|
// The next two lines are a workaround for testing without transceiver.
|
||||||
// To synchronise to the bus the RX input needs to see a high level.
|
// To synchronise to the bus the RX input needs to see a high level.
|
||||||
// Use `mem::forget()` to release the borrow on the pin but keep the
|
// Use `mem::forget()` to release the borrow on the pin but keep the
|
||||||
// pull-up resistor enabled.
|
// pull-up resistor enabled.
|
||||||
let rx_pin = Input::new(&mut p.PD0, Pull::Up);
|
let rx_pin = Input::new(&mut rx, Pull::Up);
|
||||||
core::mem::forget(rx_pin);
|
core::mem::forget(rx_pin);
|
||||||
|
|
||||||
let mut can = Can::new(p.CAN1, p.PD0, p.PD1, Irqs);
|
let mut can = Can::new(can, rx, tx, Irqs);
|
||||||
|
|
||||||
info!("Configuring can...");
|
info!("Configuring can...");
|
||||||
|
|
||||||
|
@ -6,13 +6,16 @@
|
|||||||
|
|
||||||
#[path = "../common.rs"]
|
#[path = "../common.rs"]
|
||||||
mod common;
|
mod common;
|
||||||
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
use common::*;
|
use common::*;
|
||||||
use defmt::assert;
|
use defmt::assert;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::adc::Adc;
|
use embassy_stm32::adc::Adc;
|
||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
use embassy_stm32::dac::{DacCh1, Value};
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use embassy_time::{Delay, Timer};
|
use embassy_time::{Delay, Timer};
|
||||||
|
use micromath::F32Ext;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
@ -20,31 +23,27 @@ async fn main(_spawner: Spawner) {
|
|||||||
// Initialize the board and obtain a Peripherals instance
|
// Initialize the board and obtain a Peripherals instance
|
||||||
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
|
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
|
||||||
|
|
||||||
#[cfg(feature = "stm32f429zi")]
|
let dac = peri!(p, DAC);
|
||||||
let dac_peripheral = p.DAC;
|
let dac_pin = peri!(p, DAC_PIN);
|
||||||
|
let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };
|
||||||
#[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))]
|
|
||||||
let dac_peripheral = p.DAC1;
|
|
||||||
|
|
||||||
let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4);
|
|
||||||
unwrap!(dac.set_trigger_enable(false));
|
|
||||||
|
|
||||||
|
let mut dac = DacCh1::new(dac, NoDma, dac_pin);
|
||||||
let mut adc = Adc::new(p.ADC1, &mut Delay);
|
let mut adc = Adc::new(p.ADC1, &mut Delay);
|
||||||
|
|
||||||
#[cfg(feature = "stm32h755zi")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
let normalization_factor = 256;
|
let normalization_factor = 256;
|
||||||
#[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))]
|
#[cfg(any(feature = "stm32f429zi", feature = "stm32f446re", feature = "stm32g071rb"))]
|
||||||
let normalization_factor: i32 = 16;
|
let normalization_factor: i32 = 16;
|
||||||
|
|
||||||
unwrap!(dac.set(Value::Bit8(0)));
|
dac.set(Value::Bit8(0));
|
||||||
// Now wait a little to obtain a stable value
|
// Now wait a little to obtain a stable value
|
||||||
Timer::after_millis(30).await;
|
Timer::after_millis(30).await;
|
||||||
let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
|
let offset = adc.read(&mut adc_pin);
|
||||||
|
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
// First set the DAC output value
|
// First set the DAC output value
|
||||||
let dac_output_val = to_sine_wave(v);
|
let dac_output_val = to_sine_wave(v);
|
||||||
unwrap!(dac.set(Value::Bit8(dac_output_val)));
|
dac.set(Value::Bit8(dac_output_val));
|
||||||
|
|
||||||
// Now wait a little to obtain a stable value
|
// Now wait a little to obtain a stable value
|
||||||
Timer::after_millis(30).await;
|
Timer::after_millis(30).await;
|
||||||
@ -64,10 +63,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
cortex_m::asm::bkpt();
|
cortex_m::asm::bkpt();
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::f32::consts::PI;
|
|
||||||
|
|
||||||
use micromath::F32Ext;
|
|
||||||
|
|
||||||
fn to_sine_wave(v: u8) -> u8 {
|
fn to_sine_wave(v: u8) -> u8 {
|
||||||
if v >= 128 {
|
if v >= 128 {
|
||||||
// top half
|
// top half
|
||||||
|
@ -5,11 +5,12 @@
|
|||||||
#[path = "../common.rs"]
|
#[path = "../common.rs"]
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use defmt::{assert_eq, *};
|
use common::*;
|
||||||
|
use defmt::assert_eq;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
|
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
|
||||||
use embassy_stm32::time::mhz;
|
use embassy_stm32::time::mhz;
|
||||||
use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, sdmmc};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
@ -20,12 +21,8 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut config = Config::default();
|
let p = embassy_stm32::init(config());
|
||||||
config.rcc.sys_ck = Some(mhz(48));
|
|
||||||
config.rcc.pll48 = true;
|
|
||||||
let p = embassy_stm32::init(config);
|
|
||||||
|
|
||||||
#[cfg(feature = "stm32f429zi")]
|
|
||||||
let (mut sdmmc, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) =
|
let (mut sdmmc, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) =
|
||||||
(p.SDIO, p.DMA2_CH3, p.PC12, p.PD2, p.PC8, p.PC9, p.PC10, p.PC11);
|
(p.SDIO, p.DMA2_CH3, p.PC12, p.PD2, p.PC8, p.PC9, p.PC10, p.PC11);
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ teleprobe_meta::target!(b"nucleo-stm32g491re");
|
|||||||
teleprobe_meta::target!(b"nucleo-stm32g071rb");
|
teleprobe_meta::target!(b"nucleo-stm32g071rb");
|
||||||
#[cfg(feature = "stm32f429zi")]
|
#[cfg(feature = "stm32f429zi")]
|
||||||
teleprobe_meta::target!(b"nucleo-stm32f429zi");
|
teleprobe_meta::target!(b"nucleo-stm32f429zi");
|
||||||
|
#[cfg(feature = "stm32f446re")]
|
||||||
|
teleprobe_meta::target!(b"weact-stm32f446re");
|
||||||
#[cfg(feature = "stm32wb55rg")]
|
#[cfg(feature = "stm32wb55rg")]
|
||||||
teleprobe_meta::target!(b"nucleo-stm32wb55rg");
|
teleprobe_meta::target!(b"nucleo-stm32wb55rg");
|
||||||
#[cfg(feature = "stm32h755zi")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
@ -99,14 +101,25 @@ define_peris!(
|
|||||||
define_peris!(
|
define_peris!(
|
||||||
UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
||||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
|
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,
|
||||||
|
DAC = DAC1, DAC_PIN = PA4,
|
||||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||||
);
|
);
|
||||||
#[cfg(feature = "stm32f429zi")]
|
#[cfg(feature = "stm32f429zi")]
|
||||||
define_peris!(
|
define_peris!(
|
||||||
UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,
|
UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,
|
||||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
|
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
|
||||||
|
DAC = DAC, DAC_PIN = PA4,
|
||||||
|
CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1,
|
||||||
@irq UART = {USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;},
|
@irq UART = {USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;},
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "stm32f446re")]
|
||||||
|
define_peris!(
|
||||||
|
UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5,
|
||||||
|
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,
|
||||||
|
DAC = DAC, DAC_PIN = PA4,
|
||||||
|
CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12,
|
||||||
|
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||||
|
);
|
||||||
#[cfg(feature = "stm32wb55rg")]
|
#[cfg(feature = "stm32wb55rg")]
|
||||||
define_peris!(
|
define_peris!(
|
||||||
UART = LPUART1, UART_TX = PA2, UART_RX = PA3, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
UART = LPUART1, UART_TX = PA2, UART_RX = PA3, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
||||||
@ -117,6 +130,7 @@ define_peris!(
|
|||||||
define_peris!(
|
define_peris!(
|
||||||
UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,
|
UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,
|
||||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,
|
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,
|
||||||
|
DAC = DAC1, DAC_PIN = PA4,
|
||||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||||
);
|
);
|
||||||
#[cfg(feature = "stm32h7a3zi")]
|
#[cfg(feature = "stm32h7a3zi")]
|
||||||
@ -282,6 +296,27 @@ pub fn config() -> Config {
|
|||||||
config.rcc.sys = Sysclk::PLL1_P;
|
config.rcc.sys = Sysclk::PLL1_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32f446re")]
|
||||||
|
{
|
||||||
|
use embassy_stm32::rcc::*;
|
||||||
|
config.rcc.hse = Some(Hse {
|
||||||
|
freq: Hertz(8_000_000),
|
||||||
|
mode: HseMode::Oscillator,
|
||||||
|
});
|
||||||
|
config.rcc.pll_src = PllSource::HSE;
|
||||||
|
config.rcc.pll = Some(Pll {
|
||||||
|
prediv: PllPreDiv::DIV4,
|
||||||
|
mul: PllMul::MUL168,
|
||||||
|
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168 Mhz.
|
||||||
|
divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48 Mhz.
|
||||||
|
divr: None,
|
||||||
|
});
|
||||||
|
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||||
|
config.rcc.apb1_pre = APBPrescaler::DIV4;
|
||||||
|
config.rcc.apb2_pre = APBPrescaler::DIV2;
|
||||||
|
config.rcc.sys = Sysclk::PLL1_P;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "stm32f767zi")]
|
#[cfg(feature = "stm32f767zi")]
|
||||||
{
|
{
|
||||||
use embassy_stm32::rcc::*;
|
use embassy_stm32::rcc::*;
|
||||||
|
Reference in New Issue
Block a user