Compare commits
23 Commits
cyw43-docs
...
enable-f44
Author | SHA1 | Date | |
---|---|---|---|
72414fc2c8 | |||
97e919ea64 | |||
c7841a37fa | |||
589a16b255 | |||
4567b87482 | |||
12de90e13d | |||
871ed538b1 | |||
efd5dbe019 | |||
9d46ee0758 | |||
e5912972ec | |||
c8c8b89104 | |||
189b15c426 | |||
41c3c26beb | |||
7ec1ed4de3 | |||
6564c04531 | |||
f97ef61ef8 | |||
9ddf8b08e4 | |||
71584409d9 | |||
1ea87ec6e7 | |||
7d9a76da00 | |||
c995732b0e | |||
3e2e109437 | |||
254d587385 |
3
ci.sh
3
ci.sh
@ -213,9 +213,6 @@ 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
|
||||||
|
|
||||||
|
@ -26,25 +26,22 @@ features = ["defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
digest = "0.10"
|
digest = "0.10"
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
ed25519-dalek = { version = "2", default_features = false, features = ["digest"], optional = true }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.1" }
|
||||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
salty = { version = "0.3", optional = true }
|
||||||
signature = { version = "1.6.4", default-features = false }
|
signature = { version = "2.0", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
|
rand = "0.8"
|
||||||
futures = { version = "0.3", features = ["executor"] }
|
futures = { version = "0.3", features = ["executor"] }
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.5"
|
||||||
critical-section = { version = "1.1.1", features = ["std"] }
|
critical-section = { version = "1.1.1", features = ["std"] }
|
||||||
|
ed25519-dalek = { version = "2", default_features = false, features = ["std", "rand_core", "digest"] }
|
||||||
[dev-dependencies.ed25519-dalek]
|
|
||||||
default_features = false
|
|
||||||
features = ["rand", "std", "u32_backend"]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use digest::typenum::U64;
|
use digest::typenum::U64;
|
||||||
use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
|
use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
|
||||||
use ed25519_dalek::Digest as _;
|
use ed25519_dalek::Digest;
|
||||||
|
|
||||||
pub struct Sha512(ed25519_dalek::Sha512);
|
pub struct Sha512(ed25519_dalek::Sha512);
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ impl Default for Sha512 {
|
|||||||
|
|
||||||
impl Update for Sha512 {
|
impl Update for Sha512 {
|
||||||
fn update(&mut self, data: &[u8]) {
|
fn update(&mut self, data: &[u8]) {
|
||||||
self.0.update(data)
|
Digest::update(&mut self.0, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub async fn verify_and_mark_updated(
|
pub async fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8; 32],
|
||||||
_signature: &[u8],
|
_signature: &[u8; 64],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
@ -89,14 +89,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
||||||
|
|
||||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||||
|
|
||||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||||
|
|
||||||
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
let signature = Signature::from_bytes(_signature);
|
||||||
|
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
@ -106,7 +106,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "ed25519-salty")]
|
#[cfg(feature = "ed25519-salty")]
|
||||||
{
|
{
|
||||||
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
|
||||||
use salty::{PublicKey, Signature};
|
use salty::{PublicKey, Signature};
|
||||||
|
|
||||||
use crate::digest_adapters::salty::Sha512;
|
use crate::digest_adapters::salty::Sha512;
|
||||||
@ -115,10 +114,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
||||||
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
||||||
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
|
||||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
|
@ -86,8 +86,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub fn verify_and_mark_updated(
|
pub fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8; 32],
|
||||||
_signature: &[u8],
|
_signature: &[u8; 64],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
@ -96,14 +96,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
||||||
|
|
||||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||||
|
|
||||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||||
|
|
||||||
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
let signature = Signature::from_bytes(_signature);
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
@ -113,7 +113,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "ed25519-salty")]
|
#[cfg(feature = "ed25519-salty")]
|
||||||
{
|
{
|
||||||
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
|
||||||
use salty::{PublicKey, Signature};
|
use salty::{PublicKey, Signature};
|
||||||
|
|
||||||
use crate::digest_adapters::salty::Sha512;
|
use crate::digest_adapters::salty::Sha512;
|
||||||
@ -122,10 +121,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
||||||
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
||||||
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
|
||||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
|
@ -275,21 +275,19 @@ mod tests {
|
|||||||
// The following key setup is based on:
|
// The following key setup is based on:
|
||||||
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
||||||
|
|
||||||
use ed25519_dalek::Keypair;
|
use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey};
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
let mut csprng = OsRng {};
|
let mut csprng = OsRng {};
|
||||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
let keypair = SigningKey::generate(&mut csprng);
|
||||||
|
|
||||||
use ed25519_dalek::{Digest, Sha512, Signature, Signer};
|
|
||||||
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
||||||
let mut digest = Sha512::new();
|
let mut digest = Sha512::new();
|
||||||
digest.update(&firmware);
|
digest.update(&firmware);
|
||||||
let message = digest.finalize();
|
let message = digest.finalize();
|
||||||
let signature: Signature = keypair.sign(&message);
|
let signature: Signature = keypair.sign(&message);
|
||||||
|
|
||||||
use ed25519_dalek::PublicKey;
|
let public_key = keypair.verifying_key();
|
||||||
let public_key: PublicKey = keypair.public;
|
|
||||||
|
|
||||||
// Setup flash
|
// Setup flash
|
||||||
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
||||||
|
@ -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-91cee0d1fdcb4e447b65a09756b506f4af91b7e2" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2234f380f51d16d0398b8e547088b33ea623cc7c" }
|
||||||
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-91cee0d1fdcb4e447b65a09756b506f4af91b7e2", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2234f380f51d16d0398b8e547088b33ea623cc7c", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -303,20 +303,14 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
ch.cr().write(|w| {
|
ch.cr().write(|w| {
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
if incr_mem {
|
w.set_minc(incr_mem);
|
||||||
w.set_minc(vals::Inc::ENABLED);
|
|
||||||
} else {
|
|
||||||
w.set_minc(vals::Inc::DISABLED);
|
|
||||||
}
|
|
||||||
w.set_dir(dir.into());
|
w.set_dir(dir.into());
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_tcie(options.complete_transfer_ir);
|
w.set_tcie(options.complete_transfer_ir);
|
||||||
w.set_htie(options.half_transfer_ir);
|
w.set_htie(options.half_transfer_ir);
|
||||||
|
w.set_circ(options.circular);
|
||||||
if options.circular {
|
if options.circular {
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
|
||||||
debug!("Setting circular mode");
|
debug!("Setting circular mode");
|
||||||
} else {
|
|
||||||
w.set_circ(vals::Circ::DISABLED);
|
|
||||||
}
|
}
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_en(true);
|
w.set_en(true);
|
||||||
@ -352,7 +346,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
let en = ch.cr().read().en();
|
let en = ch.cr().read().en();
|
||||||
let circular = ch.cr().read().circ() == vals::Circ::ENABLED;
|
let circular = ch.cr().read().circ();
|
||||||
let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
|
let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
|
||||||
en && (circular || !tcif)
|
en && (circular || !tcif)
|
||||||
}
|
}
|
||||||
@ -467,12 +461,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
let mut w = regs::Cr(0);
|
let mut w = regs::Cr(0);
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_minc(vals::Inc::ENABLED);
|
w.set_minc(true);
|
||||||
w.set_dir(dir.into());
|
w.set_dir(dir.into());
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_htie(true);
|
w.set_htie(true);
|
||||||
w.set_tcie(true);
|
w.set_tcie(true);
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
w.set_circ(true);
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_en(true);
|
w.set_en(true);
|
||||||
|
|
||||||
@ -625,12 +619,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
let mut w = regs::Cr(0);
|
let mut w = regs::Cr(0);
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_minc(vals::Inc::ENABLED);
|
w.set_minc(true);
|
||||||
w.set_dir(dir.into());
|
w.set_dir(dir.into());
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_htie(true);
|
w.set_htie(true);
|
||||||
w.set_tcie(true);
|
w.set_tcie(true);
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
w.set_circ(true);
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_en(true);
|
w.set_en(true);
|
||||||
|
|
||||||
|
@ -382,18 +382,13 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_minc(match incr_mem {
|
w.set_minc(incr_mem);
|
||||||
true => vals::Inc::INCREMENTED,
|
w.set_pinc(false);
|
||||||
false => vals::Inc::FIXED,
|
|
||||||
});
|
|
||||||
w.set_pinc(vals::Inc::FIXED);
|
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_tcie(options.complete_transfer_ir);
|
w.set_tcie(options.complete_transfer_ir);
|
||||||
|
w.set_circ(options.circular);
|
||||||
if options.circular {
|
if options.circular {
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
|
||||||
debug!("Setting circular mode");
|
debug!("Setting circular mode");
|
||||||
} else {
|
|
||||||
w.set_circ(vals::Circ::DISABLED);
|
|
||||||
}
|
}
|
||||||
#[cfg(dma_v1)]
|
#[cfg(dma_v1)]
|
||||||
w.set_trbuff(true);
|
w.set_trbuff(true);
|
||||||
@ -545,8 +540,8 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
|||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_minc(vals::Inc::INCREMENTED);
|
w.set_minc(true);
|
||||||
w.set_pinc(vals::Inc::FIXED);
|
w.set_pinc(false);
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_tcie(true);
|
w.set_tcie(true);
|
||||||
#[cfg(dma_v1)]
|
#[cfg(dma_v1)]
|
||||||
@ -703,12 +698,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_minc(vals::Inc::INCREMENTED);
|
w.set_minc(true);
|
||||||
w.set_pinc(vals::Inc::FIXED);
|
w.set_pinc(false);
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_htie(options.half_transfer_ir);
|
w.set_htie(options.half_transfer_ir);
|
||||||
w.set_tcie(true);
|
w.set_tcie(true);
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
w.set_circ(true);
|
||||||
#[cfg(dma_v1)]
|
#[cfg(dma_v1)]
|
||||||
w.set_trbuff(true);
|
w.set_trbuff(true);
|
||||||
#[cfg(dma_v2)]
|
#[cfg(dma_v2)]
|
||||||
@ -878,12 +873,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
w.set_msize(data_size.into());
|
w.set_msize(data_size.into());
|
||||||
w.set_psize(data_size.into());
|
w.set_psize(data_size.into());
|
||||||
w.set_pl(vals::Pl::VERYHIGH);
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_minc(vals::Inc::INCREMENTED);
|
w.set_minc(true);
|
||||||
w.set_pinc(vals::Inc::FIXED);
|
w.set_pinc(false);
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_htie(options.half_transfer_ir);
|
w.set_htie(options.half_transfer_ir);
|
||||||
w.set_tcie(true);
|
w.set_tcie(true);
|
||||||
w.set_circ(vals::Circ::ENABLED);
|
w.set_circ(true);
|
||||||
#[cfg(dma_v1)]
|
#[cfg(dma_v1)]
|
||||||
w.set_trbuff(true);
|
w.set_trbuff(true);
|
||||||
#[cfg(dma_v2)]
|
#[cfg(dma_v2)]
|
||||||
|
@ -16,6 +16,7 @@ use crate::interrupt::Priority;
|
|||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::pac::gpdma::vals;
|
use crate::pac::gpdma::vals;
|
||||||
|
|
||||||
|
/// GPDMA transfer options.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -113,10 +114,13 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA request type alias. (also known as DMA channel number in some chips)
|
||||||
pub type Request = u8;
|
pub type Request = u8;
|
||||||
|
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(not(dmamux))]
|
#[cfg(not(dmamux))]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||||
|
|
||||||
@ -131,12 +135,14 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA transfer.
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct Transfer<'a, C: Channel> {
|
pub struct Transfer<'a, C: Channel> {
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel> Transfer<'a, C> {
|
impl<'a, C: Channel> Transfer<'a, C> {
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory).
|
||||||
pub unsafe fn new_read<W: Word>(
|
pub unsafe fn new_read<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -147,6 +153,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory), using raw pointers.
|
||||||
pub unsafe fn new_read_raw<W: Word>(
|
pub unsafe fn new_read_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -172,6 +179,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral).
|
||||||
pub unsafe fn new_write<W: Word>(
|
pub unsafe fn new_write<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -182,6 +190,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), using raw pointers.
|
||||||
pub unsafe fn new_write_raw<W: Word>(
|
pub unsafe fn new_write_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -207,6 +216,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
|
||||||
pub unsafe fn new_write_repeated<W: Word>(
|
pub unsafe fn new_write_repeated<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -297,6 +307,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request the transfer to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.cr().modify(|w| {
|
ch.cr().modify(|w| {
|
||||||
@ -304,6 +317,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this transfer is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
let sr = ch.sr().read();
|
let sr = ch.sr().read();
|
||||||
@ -317,6 +334,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
ch.br1().read().bndt()
|
ch.br1().read().bndt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking wait until the transfer finishes.
|
||||||
pub fn blocking_wait(mut self) {
|
pub fn blocking_wait(mut self) {
|
||||||
while self.is_running() {}
|
while self.is_running() {}
|
||||||
|
|
||||||
|
@ -15,38 +15,42 @@ use crate::rcc::get_freqs;
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
pub enum Source {
|
/// HRTIM burst controller instance.
|
||||||
Master,
|
|
||||||
ChA,
|
|
||||||
ChB,
|
|
||||||
ChC,
|
|
||||||
ChD,
|
|
||||||
ChE,
|
|
||||||
#[cfg(hrtim_v2)]
|
|
||||||
ChF,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BurstController<T: Instance> {
|
pub struct BurstController<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM master instance.
|
||||||
pub struct Master<T: Instance> {
|
pub struct Master<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel A instance.
|
||||||
pub struct ChA<T: Instance> {
|
pub struct ChA<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel B instance.
|
||||||
pub struct ChB<T: Instance> {
|
pub struct ChB<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel C instance.
|
||||||
pub struct ChC<T: Instance> {
|
pub struct ChC<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel D instance.
|
||||||
pub struct ChD<T: Instance> {
|
pub struct ChD<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel E instance.
|
||||||
pub struct ChE<T: Instance> {
|
pub struct ChE<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM channel F instance.
|
||||||
#[cfg(hrtim_v2)]
|
#[cfg(hrtim_v2)]
|
||||||
pub struct ChF<T: Instance> {
|
pub struct ChF<T: Instance> {
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
@ -60,22 +64,26 @@ mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advanced channel instance trait.
|
||||||
pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {}
|
pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {}
|
||||||
|
|
||||||
pub struct PwmPin<'d, Perip, Channel> {
|
/// HRTIM PWM pin.
|
||||||
|
pub struct PwmPin<'d, T, C> {
|
||||||
_pin: PeripheralRef<'d, AnyPin>,
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
phantom: PhantomData<(Perip, Channel)>,
|
phantom: PhantomData<(T, C)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComplementaryPwmPin<'d, Perip, Channel> {
|
/// HRTIM complementary PWM pin.
|
||||||
|
pub struct ComplementaryPwmPin<'d, T, C> {
|
||||||
_pin: PeripheralRef<'d, AnyPin>,
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
phantom: PhantomData<(Perip, Channel)>,
|
phantom: PhantomData<(T, C)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! advanced_channel_impl {
|
macro_rules! advanced_channel_impl {
|
||||||
($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => {
|
($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => {
|
||||||
impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel<Perip>> {
|
impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> {
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self {
|
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
pin.set_low();
|
pin.set_low();
|
||||||
@ -90,8 +98,9 @@ macro_rules! advanced_channel_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel<Perip>> {
|
impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> {
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
|
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<T>> + 'd) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
pin.set_low();
|
pin.set_low();
|
||||||
@ -126,18 +135,29 @@ advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin);
|
|||||||
/// Struct used to divide a high resolution timer into multiple channels
|
/// Struct used to divide a high resolution timer into multiple channels
|
||||||
pub struct AdvancedPwm<'d, T: Instance> {
|
pub struct AdvancedPwm<'d, T: Instance> {
|
||||||
_inner: PeripheralRef<'d, T>,
|
_inner: PeripheralRef<'d, T>,
|
||||||
|
/// Master instance.
|
||||||
pub master: Master<T>,
|
pub master: Master<T>,
|
||||||
|
/// Burst controller.
|
||||||
pub burst_controller: BurstController<T>,
|
pub burst_controller: BurstController<T>,
|
||||||
|
/// Channel A.
|
||||||
pub ch_a: ChA<T>,
|
pub ch_a: ChA<T>,
|
||||||
|
/// Channel B.
|
||||||
pub ch_b: ChB<T>,
|
pub ch_b: ChB<T>,
|
||||||
|
/// Channel C.
|
||||||
pub ch_c: ChC<T>,
|
pub ch_c: ChC<T>,
|
||||||
|
/// Channel D.
|
||||||
pub ch_d: ChD<T>,
|
pub ch_d: ChD<T>,
|
||||||
|
/// Channel E.
|
||||||
pub ch_e: ChE<T>,
|
pub ch_e: ChE<T>,
|
||||||
|
/// Channel F.
|
||||||
#[cfg(hrtim_v2)]
|
#[cfg(hrtim_v2)]
|
||||||
pub ch_f: ChF<T>,
|
pub ch_f: ChF<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> AdvancedPwm<'d, T> {
|
impl<'d, T: Instance> AdvancedPwm<'d, T> {
|
||||||
|
/// Create a new HRTIM driver.
|
||||||
|
///
|
||||||
|
/// This splits the HRTIM into its constituent parts, which you can then use individually.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tim: impl Peripheral<P = T> + 'd,
|
tim: impl Peripheral<P = T> + 'd,
|
||||||
_cha: Option<PwmPin<'d, T, ChA<T>>>,
|
_cha: Option<PwmPin<'d, T, ChA<T>>>,
|
||||||
@ -200,13 +220,7 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Instance> BurstController<T> {
|
/// Fixed-frequency bridge converter driver.
|
||||||
pub fn set_source(&mut self, _source: Source) {
|
|
||||||
todo!("burst mode control registers not implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a fixed-frequency bridge converter
|
|
||||||
///
|
///
|
||||||
/// Our implementation of the bridge converter uses a single channel and three compare registers,
|
/// Our implementation of the bridge converter uses a single channel and three compare registers,
|
||||||
/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous
|
/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous
|
||||||
@ -225,6 +239,7 @@ pub struct BridgeConverter<T: Instance, C: AdvancedChannel<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
||||||
|
/// Create a new HRTIM bridge converter driver.
|
||||||
pub fn new(_channel: C, frequency: Hertz) -> Self {
|
pub fn new(_channel: C, frequency: Hertz) -> Self {
|
||||||
use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect};
|
use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect};
|
||||||
|
|
||||||
@ -281,14 +296,17 @@ impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start HRTIM.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true));
|
T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stop HRTIM.
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false));
|
T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable burst mode.
|
||||||
pub fn enable_burst_mode(&mut self) {
|
pub fn enable_burst_mode(&mut self) {
|
||||||
T::regs().tim(C::raw()).outr().modify(|w| {
|
T::regs().tim(C::raw()).outr().modify(|w| {
|
||||||
// Enable Burst Mode
|
// Enable Burst Mode
|
||||||
@ -301,6 +319,7 @@ impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable burst mode.
|
||||||
pub fn disable_burst_mode(&mut self) {
|
pub fn disable_burst_mode(&mut self) {
|
||||||
T::regs().tim(C::raw()).outr().modify(|w| {
|
T::regs().tim(C::raw()).outr().modify(|w| {
|
||||||
// Disable Burst Mode
|
// Disable Burst Mode
|
||||||
@ -357,7 +376,7 @@ impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a variable-frequency resonant converter
|
/// Variable-frequency resonant converter driver.
|
||||||
///
|
///
|
||||||
/// This implementation of a resonsant converter is appropriate for a half or full bridge,
|
/// This implementation of a resonsant converter is appropriate for a half or full bridge,
|
||||||
/// but does not include secondary rectification, which is appropriate for applications
|
/// but does not include secondary rectification, which is appropriate for applications
|
||||||
@ -370,6 +389,7 @@ pub struct ResonantConverter<T: Instance, C: AdvancedChannel<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Instance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
|
impl<T: Instance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
|
||||||
|
/// Create a new variable-frequency resonant converter driver.
|
||||||
pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
|
pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
|
||||||
T::set_channel_frequency(C::raw(), min_frequency);
|
T::set_channel_frequency(C::raw(), min_frequency);
|
||||||
|
|
||||||
@ -408,6 +428,7 @@ impl<T: Instance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
|
|||||||
T::set_channel_dead_time(C::raw(), value);
|
T::set_channel_dead_time(C::raw(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the timer period.
|
||||||
pub fn set_period(&mut self, period: u16) {
|
pub fn set_period(&mut self, period: u16) {
|
||||||
assert!(period < self.max_period);
|
assert!(period < self.max_period);
|
||||||
assert!(period > self.min_period);
|
assert!(period > self.min_period);
|
||||||
|
@ -125,7 +125,6 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the dead time as a proportion of max_duty
|
/// Set the dead time as a proportion of max_duty
|
||||||
|
|
||||||
fn set_channel_dead_time(channel: usize, dead_time: u16) {
|
fn set_channel_dead_time(channel: usize, dead_time: u16) {
|
||||||
let regs = Self::regs();
|
let regs = Self::regs();
|
||||||
|
|
||||||
@ -148,13 +147,10 @@ pub(crate) mod sealed {
|
|||||||
w.set_dtr(dt_val as u16);
|
w.set_dtr(dt_val as u16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn enable_outputs(enable: bool);
|
|
||||||
//
|
|
||||||
// fn enable_channel(&mut self, channel: usize, enable: bool);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HRTIM instance trait.
|
||||||
pub trait Instance: sealed::Instance + 'static {}
|
pub trait Instance: sealed::Instance + 'static {}
|
||||||
|
|
||||||
foreach_interrupt! {
|
foreach_interrupt! {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Inter-Process Communication Controller (IPCC)
|
||||||
|
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
@ -41,6 +43,7 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for Receive
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TX interrupt handler.
|
||||||
pub struct TransmitInterruptHandler {}
|
pub struct TransmitInterruptHandler {}
|
||||||
|
|
||||||
impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
|
impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
|
||||||
@ -72,6 +75,7 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for Transmi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// IPCC config.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -79,6 +83,8 @@ pub struct Config {
|
|||||||
// reserved for future use
|
// reserved for future use
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Channel.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum IpccChannel {
|
pub enum IpccChannel {
|
||||||
@ -90,9 +96,11 @@ pub enum IpccChannel {
|
|||||||
Channel6 = 5,
|
Channel6 = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// IPCC driver.
|
||||||
pub struct Ipcc;
|
pub struct Ipcc;
|
||||||
|
|
||||||
impl Ipcc {
|
impl Ipcc {
|
||||||
|
/// Enable IPCC.
|
||||||
pub fn enable(_config: Config) {
|
pub fn enable(_config: Config) {
|
||||||
IPCC::enable_and_reset();
|
IPCC::enable_and_reset();
|
||||||
IPCC::set_cpu2(true);
|
IPCC::set_cpu2(true);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![allow(async_fn_in_trait)]
|
#![allow(async_fn_in_trait)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! ## Feature flags
|
//! ## Feature flags
|
||||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||||
@ -79,6 +80,7 @@ pub(crate) mod _generated {
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,53 @@
|
|||||||
/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
|
//! Low-power support.
|
||||||
/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
|
//!
|
||||||
/// can use knowledge of which peripherals are currently blocked upon to transparently and safely
|
//! The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
|
||||||
/// enter such low-power modes (currently, only `STOP2`) when idle.
|
//! to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
|
||||||
///
|
//! can use knowledge of which peripherals are currently blocked upon to transparently and safely
|
||||||
/// The executor determines which peripherals are active by their RCC state; consequently,
|
//! enter such low-power modes (currently, only `STOP2`) when idle.
|
||||||
/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few
|
//!
|
||||||
/// exceptions to this rule:
|
//! The executor determines which peripherals are active by their RCC state; consequently,
|
||||||
///
|
//! low-power states can only be entered if all peripherals have been `drop`'d. There are a few
|
||||||
/// * `GPIO`
|
//! exceptions to this rule:
|
||||||
/// * `RCC`
|
//!
|
||||||
///
|
//! * `GPIO`
|
||||||
/// Since entering and leaving low-power modes typically incurs a significant latency, the
|
//! * `RCC`
|
||||||
/// low-power executor will only attempt to enter when the next timer event is at least
|
//!
|
||||||
/// [`time_driver::MIN_STOP_PAUSE`] in the future.
|
//! Since entering and leaving low-power modes typically incurs a significant latency, the
|
||||||
///
|
//! low-power executor will only attempt to enter when the next timer event is at least
|
||||||
/// Currently there is no macro analogous to `embassy_executor::main` for this executor;
|
//! [`time_driver::MIN_STOP_PAUSE`] in the future.
|
||||||
/// consequently one must define their entrypoint manually. Moveover, you must relinquish control
|
//!
|
||||||
/// of the `RTC` peripheral to the executor. This will typically look like
|
//! Currently there is no macro analogous to `embassy_executor::main` for this executor;
|
||||||
///
|
//! consequently one must define their entrypoint manually. Moveover, you must relinquish control
|
||||||
/// ```rust,no_run
|
//! of the `RTC` peripheral to the executor. This will typically look like
|
||||||
/// use embassy_executor::Spawner;
|
//!
|
||||||
/// use embassy_stm32::low_power::Executor;
|
//! ```rust,no_run
|
||||||
/// use embassy_stm32::rtc::{Rtc, RtcConfig};
|
//! use embassy_executor::Spawner;
|
||||||
/// use static_cell::make_static;
|
//! use embassy_stm32::low_power::Executor;
|
||||||
///
|
//! use embassy_stm32::rtc::{Rtc, RtcConfig};
|
||||||
/// #[cortex_m_rt::entry]
|
//! use static_cell::make_static;
|
||||||
/// fn main() -> ! {
|
//!
|
||||||
/// Executor::take().run(|spawner| {
|
//! #[cortex_m_rt::entry]
|
||||||
/// unwrap!(spawner.spawn(async_main(spawner)));
|
//! fn main() -> ! {
|
||||||
/// });
|
//! Executor::take().run(|spawner| {
|
||||||
/// }
|
//! unwrap!(spawner.spawn(async_main(spawner)));
|
||||||
///
|
//! });
|
||||||
/// #[embassy_executor::task]
|
//! }
|
||||||
/// async fn async_main(spawner: Spawner) {
|
//!
|
||||||
/// // initialize the platform...
|
//! #[embassy_executor::task]
|
||||||
/// let mut config = embassy_stm32::Config::default();
|
//! async fn async_main(spawner: Spawner) {
|
||||||
/// let p = embassy_stm32::init(config);
|
//! // initialize the platform...
|
||||||
///
|
//! let mut config = embassy_stm32::Config::default();
|
||||||
/// // give the RTC to the executor...
|
//! let p = embassy_stm32::init(config);
|
||||||
/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
|
//!
|
||||||
/// let rtc = make_static!(rtc);
|
//! // give the RTC to the executor...
|
||||||
/// embassy_stm32::low_power::stop_with_rtc(rtc);
|
//! let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
|
||||||
///
|
//! let rtc = make_static!(rtc);
|
||||||
/// // your application here...
|
//! embassy_stm32::low_power::stop_with_rtc(rtc);
|
||||||
/// }
|
//!
|
||||||
/// ```
|
//! // your application here...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
@ -64,6 +67,7 @@ static mut EXECUTOR: Option<Executor> = None;
|
|||||||
foreach_interrupt! {
|
foreach_interrupt! {
|
||||||
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
|
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
unsafe fn $irq() {
|
unsafe fn $irq() {
|
||||||
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
||||||
}
|
}
|
||||||
@ -75,10 +79,15 @@ pub(crate) unsafe fn on_wakeup_irq() {
|
|||||||
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure STOP mode with RTC.
|
||||||
pub fn stop_with_rtc(rtc: &'static Rtc) {
|
pub fn stop_with_rtc(rtc: &'static Rtc) {
|
||||||
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the core is ready to enter the given stop mode.
|
||||||
|
///
|
||||||
|
/// This will return false if some peripheral driver is in use that
|
||||||
|
/// prevents entering the given stop mode.
|
||||||
pub fn stop_ready(stop_mode: StopMode) -> bool {
|
pub fn stop_ready(stop_mode: StopMode) -> bool {
|
||||||
match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() {
|
match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() {
|
||||||
Some(StopMode::Stop2) => true,
|
Some(StopMode::Stop2) => true,
|
||||||
@ -87,10 +96,13 @@ pub fn stop_ready(stop_mode: StopMode) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Available stop modes.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum StopMode {
|
pub enum StopMode {
|
||||||
|
/// STOP 1
|
||||||
Stop1,
|
Stop1,
|
||||||
|
/// STOP 2
|
||||||
Stop2,
|
Stop2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
//! Operational Amplifier (OPAMP)
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
/// Gain
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OpAmpGain {
|
pub enum OpAmpGain {
|
||||||
Mul1,
|
Mul1,
|
||||||
@ -13,6 +16,8 @@ pub enum OpAmpGain {
|
|||||||
Mul16,
|
Mul16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Speed
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OpAmpSpeed {
|
pub enum OpAmpSpeed {
|
||||||
Normal,
|
Normal,
|
||||||
@ -180,6 +185,7 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opamp instance trait.
|
||||||
pub trait Instance: sealed::Instance + 'static {}
|
pub trait Instance: sealed::Instance + 'static {}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
@ -198,8 +204,11 @@ pub(crate) mod sealed {
|
|||||||
pub trait OutputPin<T: Instance> {}
|
pub trait OutputPin<T: Instance> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-inverting pin trait.
|
||||||
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
|
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
|
||||||
|
/// Inverting pin trait.
|
||||||
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
||||||
|
/// Output pin trait.
|
||||||
pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
|
pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
|
||||||
|
|
||||||
macro_rules! impl_opamp_external_output {
|
macro_rules! impl_opamp_external_output {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Enums used in QSPI configuration.
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub(crate) enum QspiMode {
|
pub(crate) enum QspiMode {
|
||||||
|
@ -14,6 +14,7 @@ use crate::pac::quadspi::Quadspi as Regs;
|
|||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
|
/// QSPI transfer configuration.
|
||||||
pub struct TransferConfig {
|
pub struct TransferConfig {
|
||||||
/// Instraction width (IMODE)
|
/// Instraction width (IMODE)
|
||||||
pub iwidth: QspiWidth,
|
pub iwidth: QspiWidth,
|
||||||
@ -45,6 +46,7 @@ impl Default for TransferConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI driver configuration.
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
||||||
/// If you need other value the whose predefined use `Other` variant.
|
/// If you need other value the whose predefined use `Other` variant.
|
||||||
@ -71,6 +73,7 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI driver.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Qspi<'d, T: Instance, Dma> {
|
pub struct Qspi<'d, T: Instance, Dma> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
@ -85,6 +88,7 @@ pub struct Qspi<'d, T: Instance, Dma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||||
|
/// Create a new QSPI driver for bank 1.
|
||||||
pub fn new_bk1(
|
pub fn new_bk1(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
||||||
@ -125,6 +129,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new QSPI driver for bank 2.
|
||||||
pub fn new_bk2(
|
pub fn new_bk2(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
||||||
@ -223,6 +228,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do a QSPI command.
|
||||||
pub fn command(&mut self, transaction: TransferConfig) {
|
pub fn command(&mut self, transaction: TransferConfig) {
|
||||||
#[cfg(not(stm32h7))]
|
#[cfg(not(stm32h7))]
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||||
@ -232,6 +238,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking read data.
|
||||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||||
#[cfg(not(stm32h7))]
|
#[cfg(not(stm32h7))]
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||||
@ -256,6 +263,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking write data.
|
||||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||||
// STM32H7 does not have dmaen
|
// STM32H7 does not have dmaen
|
||||||
#[cfg(not(stm32h7))]
|
#[cfg(not(stm32h7))]
|
||||||
@ -278,6 +286,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking read data, using DMA.
|
||||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||||
where
|
where
|
||||||
Dma: QuadDma<T>,
|
Dma: QuadDma<T>,
|
||||||
@ -310,6 +319,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
transfer.blocking_wait();
|
transfer.blocking_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking write data, using DMA.
|
||||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||||
where
|
where
|
||||||
Dma: QuadDma<T>,
|
Dma: QuadDma<T>,
|
||||||
@ -379,6 +389,7 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI instance trait.
|
||||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||||
|
|
||||||
pin_trait!(SckPin, Instance);
|
pin_trait!(SckPin, Instance);
|
||||||
|
@ -80,6 +80,7 @@ impl<'d, T: Instance> Rng<'d, T> {
|
|||||||
let _ = self.next_u32();
|
let _ = self.next_u32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the RNG.
|
||||||
#[cfg(not(rng_v1))]
|
#[cfg(not(rng_v1))]
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
T::regs().cr().write(|reg| {
|
T::regs().cr().write(|reg| {
|
||||||
|
@ -130,7 +130,7 @@ impl RtcTimeProvider {
|
|||||||
let weekday = day_of_week_from_u8(dr.wdu()).map_err(RtcError::InvalidDateTime)?;
|
let weekday = day_of_week_from_u8(dr.wdu()).map_err(RtcError::InvalidDateTime)?;
|
||||||
let day = bcd2_to_byte((dr.dt(), dr.du()));
|
let day = bcd2_to_byte((dr.dt(), dr.du()));
|
||||||
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
||||||
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 2000_u16;
|
||||||
|
|
||||||
DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
|
DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
|
||||||
})
|
})
|
||||||
@ -261,7 +261,7 @@ impl Rtc {
|
|||||||
let (dt, du) = byte_to_bcd2(t.day() as u8);
|
let (dt, du) = byte_to_bcd2(t.day() as u8);
|
||||||
let (mt, mu) = byte_to_bcd2(t.month() as u8);
|
let (mt, mu) = byte_to_bcd2(t.month() as u8);
|
||||||
let yr = t.year() as u16;
|
let yr = t.year() as u16;
|
||||||
let yr_offset = (yr - 1970_u16) as u8;
|
let yr_offset = (yr - 2000_u16) as u8;
|
||||||
let (yt, yu) = byte_to_bcd2(yr_offset);
|
let (yt, yu) = byte_to_bcd2(yr_offset);
|
||||||
|
|
||||||
use crate::pac::rtc::vals::Ampm;
|
use crate::pac::rtc::vals::Ampm;
|
||||||
|
@ -54,6 +54,7 @@ const SD_INIT_FREQ: Hertz = Hertz(400_000);
|
|||||||
|
|
||||||
/// The signalling scheme used on the SDMMC bus
|
/// The signalling scheme used on the SDMMC bus
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Signalling {
|
pub enum Signalling {
|
||||||
@ -70,6 +71,9 @@ impl Default for Signalling {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Aligned data block for SDMMC transfers.
|
||||||
|
///
|
||||||
|
/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements.
|
||||||
#[repr(align(4))]
|
#[repr(align(4))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -94,17 +98,23 @@ impl DerefMut for DataBlock {
|
|||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Timeout reported by the hardware
|
||||||
Timeout,
|
Timeout,
|
||||||
|
/// Timeout reported by the software driver.
|
||||||
SoftwareTimeout,
|
SoftwareTimeout,
|
||||||
|
/// Unsupported card version.
|
||||||
UnsupportedCardVersion,
|
UnsupportedCardVersion,
|
||||||
|
/// Unsupported card type.
|
||||||
UnsupportedCardType,
|
UnsupportedCardType,
|
||||||
|
/// CRC error.
|
||||||
Crc,
|
Crc,
|
||||||
DataCrcFail,
|
/// No card inserted.
|
||||||
RxOverFlow,
|
|
||||||
NoCard,
|
NoCard,
|
||||||
|
/// Bad clock supplied to the SDMMC peripheral.
|
||||||
BadClock,
|
BadClock,
|
||||||
|
/// Signaling switch failed.
|
||||||
SignalingSwitchFailed,
|
SignalingSwitchFailed,
|
||||||
PeripheralBusy,
|
/// ST bit error.
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
StBitErr,
|
StBitErr,
|
||||||
}
|
}
|
||||||
@ -283,6 +293,7 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> {
|
|||||||
|
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
||||||
|
/// Create a new SDMMC driver, with 1 data lane.
|
||||||
pub fn new_1bit(
|
pub fn new_1bit(
|
||||||
sdmmc: impl Peripheral<P = T> + 'd,
|
sdmmc: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -317,6 +328,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new SDMMC driver, with 4 data lanes.
|
||||||
pub fn new_4bit(
|
pub fn new_4bit(
|
||||||
sdmmc: impl Peripheral<P = T> + 'd,
|
sdmmc: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -363,6 +375,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
|
|||||||
|
|
||||||
#[cfg(sdmmc_v2)]
|
#[cfg(sdmmc_v2)]
|
||||||
impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
|
impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
|
||||||
|
/// Create a new SDMMC driver, with 1 data lane.
|
||||||
pub fn new_1bit(
|
pub fn new_1bit(
|
||||||
sdmmc: impl Peripheral<P = T> + 'd,
|
sdmmc: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -396,6 +409,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new SDMMC driver, with 4 data lanes.
|
||||||
pub fn new_4bit(
|
pub fn new_4bit(
|
||||||
sdmmc: impl Peripheral<P = T> + 'd,
|
sdmmc: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -497,7 +511,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Data transfer is in progress
|
/// Data transfer is in progress
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn data_active() -> bool {
|
fn data_active() -> bool {
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
|
|
||||||
@ -509,7 +523,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Coammand transfer is in progress
|
/// Coammand transfer is in progress
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn cmd_active() -> bool {
|
fn cmd_active() -> bool {
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
|
|
||||||
@ -521,7 +535,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)
|
/// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn wait_idle() {
|
fn wait_idle() {
|
||||||
while Self::data_active() || Self::cmd_active() {}
|
while Self::data_active() || Self::cmd_active() {}
|
||||||
}
|
}
|
||||||
@ -837,7 +851,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clear flags in interrupt clear register
|
/// Clear flags in interrupt clear register
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn clear_interrupt_flags() {
|
fn clear_interrupt_flags() {
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
regs.icr().write(|w| {
|
regs.icr().write(|w| {
|
||||||
@ -1152,7 +1166,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
/// Read a data block.
|
||||||
|
#[inline]
|
||||||
pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
|
pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
|
||||||
let card_capacity = self.card()?.card_type;
|
let card_capacity = self.card()?.card_type;
|
||||||
|
|
||||||
@ -1204,6 +1219,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a data block.
|
||||||
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
||||||
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
||||||
|
|
||||||
@ -1283,7 +1299,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
|||||||
///
|
///
|
||||||
/// Returns Error::NoCard if [`init_card`](#method.init_card)
|
/// Returns Error::NoCard if [`init_card`](#method.init_card)
|
||||||
/// has not previously succeeded
|
/// has not previously succeeded
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn card(&self) -> Result<&Card, Error> {
|
pub fn card(&self) -> Result<&Card, Error> {
|
||||||
self.card.as_ref().ok_or(Error::NoCard)
|
self.card.as_ref().ok_or(Error::NoCard)
|
||||||
}
|
}
|
||||||
@ -1419,7 +1435,9 @@ pub(crate) mod sealed {
|
|||||||
pub trait Pins<T: Instance> {}
|
pub trait Pins<T: Instance> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SDMMC instance trait.
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||||
|
|
||||||
pin_trait!(CkPin, Instance);
|
pin_trait!(CkPin, Instance);
|
||||||
pin_trait!(CmdPin, Instance);
|
pin_trait!(CmdPin, Instance);
|
||||||
pin_trait!(D0Pin, Instance);
|
pin_trait!(D0Pin, Instance);
|
||||||
@ -1434,7 +1452,10 @@ pin_trait!(D7Pin, Instance);
|
|||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
dma_trait!(SdmmcDma, Instance);
|
dma_trait!(SdmmcDma, Instance);
|
||||||
|
|
||||||
// SDMMCv2 uses internal DMA
|
/// DMA instance trait.
|
||||||
|
///
|
||||||
|
/// This is only implemented for `NoDma`, since SDMMCv2 has DMA built-in, instead of
|
||||||
|
/// using ST's system-wide DMA peripheral.
|
||||||
#[cfg(sdmmc_v2)]
|
#[cfg(sdmmc_v2)]
|
||||||
pub trait SdmmcDma<T: Instance> {}
|
pub trait SdmmcDma<T: Instance> {}
|
||||||
#[cfg(sdmmc_v2)]
|
#[cfg(sdmmc_v2)]
|
||||||
|
@ -16,27 +16,38 @@ use crate::rcc::RccPeripheral;
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
|
/// SPI error.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Invalid framing.
|
||||||
Framing,
|
Framing,
|
||||||
|
/// CRC error (only if hardware CRC checking is enabled).
|
||||||
Crc,
|
Crc,
|
||||||
|
/// Mode fault
|
||||||
ModeFault,
|
ModeFault,
|
||||||
|
/// Overrun.
|
||||||
Overrun,
|
Overrun,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move upwards in the tree
|
/// SPI bit order
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum BitOrder {
|
pub enum BitOrder {
|
||||||
|
/// Least significant bit first.
|
||||||
LsbFirst,
|
LsbFirst,
|
||||||
|
/// Most significant bit first.
|
||||||
MsbFirst,
|
MsbFirst,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SPI configuration.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// SPI mode.
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
|
/// Bit order.
|
||||||
pub bit_order: BitOrder,
|
pub bit_order: BitOrder,
|
||||||
|
/// Clock frequency.
|
||||||
pub frequency: Hertz,
|
pub frequency: Hertz,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +84,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SPI driver.
|
||||||
pub struct Spi<'d, T: Instance, Tx, Rx> {
|
pub struct Spi<'d, T: Instance, Tx, Rx> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
@ -84,6 +96,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||||
|
/// Create a new SPI driver.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||||
@ -118,6 +131,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI).
|
||||||
pub fn new_rxonly(
|
pub fn new_rxonly(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||||
@ -143,6 +157,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO).
|
||||||
pub fn new_txonly(
|
pub fn new_txonly(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||||
@ -168,6 +183,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new SPI driver, in TX-only mode, without SCK pin.
|
||||||
|
///
|
||||||
|
/// This can be useful for bit-banging non-SPI protocols.
|
||||||
pub fn new_txonly_nosck(
|
pub fn new_txonly_nosck(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||||
@ -355,6 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get current SPI configuration.
|
||||||
pub fn get_current_config(&self) -> Config {
|
pub fn get_current_config(&self) -> Config {
|
||||||
#[cfg(any(spi_v1, spi_f1, spi_v2))]
|
#[cfg(any(spi_v1, spi_f1, spi_v2))]
|
||||||
let cfg = T::REGS.cr1().read();
|
let cfg = T::REGS.cr1().read();
|
||||||
@ -444,6 +463,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
self.current_word_size = word_size;
|
self.current_word_size = word_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SPI write, using DMA.
|
||||||
pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
|
pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
Tx: TxDma<T>,
|
Tx: TxDma<T>,
|
||||||
@ -477,6 +497,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SPI read, using DMA.
|
||||||
pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
|
pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
Tx: TxDma<T>,
|
Tx: TxDma<T>,
|
||||||
@ -580,6 +601,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bidirectional transfer, using DMA.
|
||||||
|
///
|
||||||
|
/// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.
|
||||||
|
///
|
||||||
|
/// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
|
||||||
|
/// If `write` is shorter it is padded with zero bytes.
|
||||||
pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error>
|
pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
Tx: TxDma<T>,
|
Tx: TxDma<T>,
|
||||||
@ -588,6 +615,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
self.transfer_inner(read, write).await
|
self.transfer_inner(read, write).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// In-place bidirectional transfer, using DMA.
|
||||||
|
///
|
||||||
|
/// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
|
||||||
pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
|
pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
Tx: TxDma<T>,
|
Tx: TxDma<T>,
|
||||||
@ -596,6 +626,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
self.transfer_inner(data, data).await
|
self.transfer_inner(data, data).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking write.
|
||||||
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
|
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
|
||||||
T::REGS.cr1().modify(|w| w.set_spe(true));
|
T::REGS.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
@ -606,6 +637,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking read.
|
||||||
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||||
T::REGS.cr1().modify(|w| w.set_spe(true));
|
T::REGS.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
@ -616,6 +648,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking in-place bidirectional transfer.
|
||||||
|
///
|
||||||
|
/// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
|
||||||
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||||
T::REGS.cr1().modify(|w| w.set_spe(true));
|
T::REGS.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
@ -626,6 +661,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking bidirectional transfer.
|
||||||
|
///
|
||||||
|
/// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.
|
||||||
|
///
|
||||||
|
/// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
|
||||||
|
/// If `write` is shorter it is padded with zero bytes.
|
||||||
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
|
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
|
||||||
T::REGS.cr1().modify(|w| w.set_spe(true));
|
T::REGS.cr1().modify(|w| w.set_spe(true));
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
@ -946,6 +987,7 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Word sizes usable for SPI.
|
||||||
pub trait Word: word::Word + sealed::Word {}
|
pub trait Word: word::Word + sealed::Word {}
|
||||||
|
|
||||||
macro_rules! impl_word {
|
macro_rules! impl_word {
|
||||||
@ -1025,7 +1067,9 @@ mod word_impl {
|
|||||||
impl_word!(u32, 32 - 1);
|
impl_word!(u32, 32 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SPI instance trait.
|
||||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||||
|
|
||||||
pin_trait!(SckPin, Instance);
|
pin_trait!(SckPin, Instance);
|
||||||
pin_trait!(MosiPin, Instance);
|
pin_trait!(MosiPin, Instance);
|
||||||
pin_trait!(MisoPin, Instance);
|
pin_trait!(MisoPin, Instance);
|
||||||
|
@ -8,14 +8,17 @@ use core::ops::{Div, Mul};
|
|||||||
pub struct Hertz(pub u32);
|
pub struct Hertz(pub u32);
|
||||||
|
|
||||||
impl Hertz {
|
impl Hertz {
|
||||||
|
/// Create a `Hertz` from the given hertz.
|
||||||
pub const fn hz(hertz: u32) -> Self {
|
pub const fn hz(hertz: u32) -> Self {
|
||||||
Self(hertz)
|
Self(hertz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a `Hertz` from the given kilohertz.
|
||||||
pub const fn khz(kilohertz: u32) -> Self {
|
pub const fn khz(kilohertz: u32) -> Self {
|
||||||
Self(kilohertz * 1_000)
|
Self(kilohertz * 1_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a `Hertz` from the given megahertz.
|
||||||
pub const fn mhz(megahertz: u32) -> Self {
|
pub const fn mhz(megahertz: u32) -> Self {
|
||||||
Self(megahertz * 1_000_000)
|
Self(megahertz * 1_000_000)
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,19 @@ use crate::gpio::{AnyPin, OutputType};
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
pub struct ComplementaryPwmPin<'d, Perip, Channel> {
|
/// Complementary PWM pin wrapper.
|
||||||
|
///
|
||||||
|
/// This wraps a pin to make it usable with PWM.
|
||||||
|
pub struct ComplementaryPwmPin<'d, T, C> {
|
||||||
_pin: PeripheralRef<'d, AnyPin>,
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
phantom: PhantomData<(Perip, Channel)>,
|
phantom: PhantomData<(T, C)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! complementary_channel_impl {
|
macro_rules! complementary_channel_impl {
|
||||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||||
impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> {
|
impl<'d, T: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self {
|
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
pin.set_low();
|
pin.set_low();
|
||||||
@ -43,11 +47,13 @@ complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin);
|
|||||||
complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
|
complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
|
||||||
complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
|
complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
|
||||||
|
|
||||||
|
/// PWM driver with support for standard and complementary outputs.
|
||||||
pub struct ComplementaryPwm<'d, T> {
|
pub struct ComplementaryPwm<'d, T> {
|
||||||
inner: PeripheralRef<'d, T>,
|
inner: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
||||||
|
/// Create a new complementary PWM driver.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tim: impl Peripheral<P = T> + 'd,
|
tim: impl Peripheral<P = T> + 'd,
|
||||||
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
||||||
@ -72,7 +78,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
let mut this = Self { inner: tim };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.inner.set_counting_mode(counting_mode);
|
this.inner.set_counting_mode(counting_mode);
|
||||||
this.set_freq(freq);
|
this.set_frequency(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
this.inner.enable_outputs();
|
this.inner.enable_outputs();
|
||||||
@ -88,17 +94,23 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable the given channel.
|
||||||
pub fn enable(&mut self, channel: Channel) {
|
pub fn enable(&mut self, channel: Channel) {
|
||||||
self.inner.enable_channel(channel, true);
|
self.inner.enable_channel(channel, true);
|
||||||
self.inner.enable_complementary_channel(channel, true);
|
self.inner.enable_complementary_channel(channel, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable the given channel.
|
||||||
pub fn disable(&mut self, channel: Channel) {
|
pub fn disable(&mut self, channel: Channel) {
|
||||||
self.inner.enable_complementary_channel(channel, false);
|
self.inner.enable_complementary_channel(channel, false);
|
||||||
self.inner.enable_channel(channel, false);
|
self.inner.enable_channel(channel, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_freq(&mut self, freq: Hertz) {
|
/// Set PWM frequency.
|
||||||
|
///
|
||||||
|
/// Note: when you call this, the max duty value changes, so you will have to
|
||||||
|
/// call `set_duty` on all channels with the duty calculated based on the new max duty.
|
||||||
|
pub fn set_frequency(&mut self, freq: Hertz) {
|
||||||
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
||||||
2u8
|
2u8
|
||||||
} else {
|
} else {
|
||||||
@ -107,15 +119,22 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
self.inner.set_frequency(freq * multiplier);
|
self.inner.set_frequency(freq * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get max duty value.
|
||||||
|
///
|
||||||
|
/// This value depends on the configured frequency and the timer's clock rate from RCC.
|
||||||
pub fn get_max_duty(&self) -> u16 {
|
pub fn get_max_duty(&self) -> u16 {
|
||||||
self.inner.get_max_compare_value() + 1
|
self.inner.get_max_compare_value() + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the duty for a given channel.
|
||||||
|
///
|
||||||
|
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
|
||||||
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
|
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
|
||||||
assert!(duty <= self.get_max_duty());
|
assert!(duty <= self.get_max_duty());
|
||||||
self.inner.set_compare_value(channel, duty)
|
self.inner.set_compare_value(channel, duty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the output polarity for a given channel.
|
||||||
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||||
self.inner.set_output_polarity(channel, polarity);
|
self.inner.set_output_polarity(channel, polarity);
|
||||||
self.inner.set_complementary_output_polarity(channel, polarity);
|
self.inner.set_complementary_output_polarity(channel, polarity);
|
||||||
|
@ -17,17 +17,27 @@ pub mod low_level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// Basic 16-bit timer instance.
|
||||||
pub trait Basic16bitInstance: RccPeripheral {
|
pub trait Basic16bitInstance: RccPeripheral {
|
||||||
|
/// Interrupt for this timer.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
|
|
||||||
|
/// Get access to the basic 16bit timer registers.
|
||||||
|
///
|
||||||
|
/// Note: This works even if the timer is more capable, because registers
|
||||||
|
/// for the less capable timers are a subset. This allows writing a driver
|
||||||
|
/// for a given set of capabilities, and having it transparently work with
|
||||||
|
/// more capable timers.
|
||||||
fn regs() -> crate::pac::timer::TimBasic;
|
fn regs() -> crate::pac::timer::TimBasic;
|
||||||
|
|
||||||
|
/// Start the timer.
|
||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
Self::regs().cr1().modify(|r| r.set_cen(true));
|
Self::regs().cr1().modify(|r| r.set_cen(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stop the timer.
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
Self::regs().cr1().modify(|r| r.set_cen(false));
|
Self::regs().cr1().modify(|r| r.set_cen(false));
|
||||||
}
|
}
|
||||||
@ -63,6 +73,9 @@ pub(crate) mod sealed {
|
|||||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear update interrupt.
|
||||||
|
///
|
||||||
|
/// Returns whether the update interrupt flag was set.
|
||||||
fn clear_update_interrupt(&mut self) -> bool {
|
fn clear_update_interrupt(&mut self) -> bool {
|
||||||
let regs = Self::regs();
|
let regs = Self::regs();
|
||||||
let sr = regs.sr().read();
|
let sr = regs.sr().read();
|
||||||
@ -76,14 +89,17 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable/disable the update interrupt.
|
||||||
fn enable_update_interrupt(&mut self, enable: bool) {
|
fn enable_update_interrupt(&mut self, enable: bool) {
|
||||||
Self::regs().dier().write(|r| r.set_uie(enable));
|
Self::regs().dier().write(|r| r.set_uie(enable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable/disable autoreload preload.
|
||||||
fn set_autoreload_preload(&mut self, enable: bool) {
|
fn set_autoreload_preload(&mut self, enable: bool) {
|
||||||
Self::regs().cr1().modify(|r| r.set_arpe(enable));
|
Self::regs().cr1().modify(|r| r.set_arpe(enable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the timer frequency.
|
||||||
fn get_frequency(&self) -> Hertz {
|
fn get_frequency(&self) -> Hertz {
|
||||||
let timer_f = Self::frequency();
|
let timer_f = Self::frequency();
|
||||||
|
|
||||||
@ -95,9 +111,17 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gneral-purpose 16-bit timer instance.
|
||||||
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
||||||
|
/// Get access to the general purpose 16bit timer registers.
|
||||||
|
///
|
||||||
|
/// Note: This works even if the timer is more capable, because registers
|
||||||
|
/// for the less capable timers are a subset. This allows writing a driver
|
||||||
|
/// for a given set of capabilities, and having it transparently work with
|
||||||
|
/// more capable timers.
|
||||||
fn regs_gp16() -> crate::pac::timer::TimGp16;
|
fn regs_gp16() -> crate::pac::timer::TimGp16;
|
||||||
|
|
||||||
|
/// Set counting mode.
|
||||||
fn set_counting_mode(&mut self, mode: CountingMode) {
|
fn set_counting_mode(&mut self, mode: CountingMode) {
|
||||||
let (cms, dir) = mode.into();
|
let (cms, dir) = mode.into();
|
||||||
|
|
||||||
@ -110,19 +134,29 @@ pub(crate) mod sealed {
|
|||||||
Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
|
Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get counting mode.
|
||||||
fn get_counting_mode(&self) -> CountingMode {
|
fn get_counting_mode(&self) -> CountingMode {
|
||||||
let cr1 = Self::regs_gp16().cr1().read();
|
let cr1 = Self::regs_gp16().cr1().read();
|
||||||
(cr1.cms(), cr1.dir()).into()
|
(cr1.cms(), cr1.dir()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set clock divider.
|
||||||
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
||||||
Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd));
|
Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gneral-purpose 32-bit timer instance.
|
||||||
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
|
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
|
||||||
|
/// Get access to the general purpose 32bit timer registers.
|
||||||
|
///
|
||||||
|
/// Note: This works even if the timer is more capable, because registers
|
||||||
|
/// for the less capable timers are a subset. This allows writing a driver
|
||||||
|
/// for a given set of capabilities, and having it transparently work with
|
||||||
|
/// more capable timers.
|
||||||
fn regs_gp32() -> crate::pac::timer::TimGp32;
|
fn regs_gp32() -> crate::pac::timer::TimGp32;
|
||||||
|
|
||||||
|
/// Set timer frequency.
|
||||||
fn set_frequency(&mut self, frequency: Hertz) {
|
fn set_frequency(&mut self, frequency: Hertz) {
|
||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
assert!(f > 0);
|
assert!(f > 0);
|
||||||
@ -140,6 +174,7 @@ pub(crate) mod sealed {
|
|||||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get timer frequency.
|
||||||
fn get_frequency(&self) -> Hertz {
|
fn get_frequency(&self) -> Hertz {
|
||||||
let timer_f = Self::frequency();
|
let timer_f = Self::frequency();
|
||||||
|
|
||||||
@ -151,141 +186,177 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advanced control timer instance.
|
||||||
pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
|
pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
|
||||||
|
/// Get access to the advanced timer registers.
|
||||||
fn regs_advanced() -> crate::pac::timer::TimAdv;
|
fn regs_advanced() -> crate::pac::timer::TimAdv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capture/Compare 16-bit timer instance.
|
||||||
pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance {
|
pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance {
|
||||||
|
/// Set input capture filter.
|
||||||
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) {
|
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) {
|
||||||
let raw_channel = channel.raw();
|
let raw_channel = channel.index();
|
||||||
Self::regs_gp16()
|
Self::regs_gp16()
|
||||||
.ccmr_input(raw_channel / 2)
|
.ccmr_input(raw_channel / 2)
|
||||||
.modify(|r| r.set_icf(raw_channel % 2, icf));
|
.modify(|r| r.set_icf(raw_channel % 2, icf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear input interrupt.
|
||||||
fn clear_input_interrupt(&mut self, channel: Channel) {
|
fn clear_input_interrupt(&mut self, channel: Channel) {
|
||||||
Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.raw(), false));
|
Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable input interrupt.
|
||||||
fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) {
|
fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) {
|
||||||
Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.raw(), enable));
|
Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set input capture prescaler.
|
||||||
fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) {
|
fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) {
|
||||||
let raw_channel = channel.raw();
|
let raw_channel = channel.index();
|
||||||
Self::regs_gp16()
|
Self::regs_gp16()
|
||||||
.ccmr_input(raw_channel / 2)
|
.ccmr_input(raw_channel / 2)
|
||||||
.modify(|r| r.set_icpsc(raw_channel % 2, factor));
|
.modify(|r| r.set_icpsc(raw_channel % 2, factor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set input TI selection.
|
||||||
fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) {
|
fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) {
|
||||||
let raw_channel = channel.raw();
|
let raw_channel = channel.index();
|
||||||
Self::regs_gp16()
|
Self::regs_gp16()
|
||||||
.ccmr_input(raw_channel / 2)
|
.ccmr_input(raw_channel / 2)
|
||||||
.modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
|
.modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set input capture mode.
|
||||||
fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {
|
fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {
|
||||||
Self::regs_gp16().ccer().modify(|r| match mode {
|
Self::regs_gp16().ccer().modify(|r| match mode {
|
||||||
InputCaptureMode::Rising => {
|
InputCaptureMode::Rising => {
|
||||||
r.set_ccnp(channel.raw(), false);
|
r.set_ccnp(channel.index(), false);
|
||||||
r.set_ccp(channel.raw(), false);
|
r.set_ccp(channel.index(), false);
|
||||||
}
|
}
|
||||||
InputCaptureMode::Falling => {
|
InputCaptureMode::Falling => {
|
||||||
r.set_ccnp(channel.raw(), false);
|
r.set_ccnp(channel.index(), false);
|
||||||
r.set_ccp(channel.raw(), true);
|
r.set_ccp(channel.index(), true);
|
||||||
}
|
}
|
||||||
InputCaptureMode::BothEdges => {
|
InputCaptureMode::BothEdges => {
|
||||||
r.set_ccnp(channel.raw(), true);
|
r.set_ccnp(channel.index(), true);
|
||||||
r.set_ccp(channel.raw(), true);
|
r.set_ccp(channel.index(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable timer outputs.
|
||||||
fn enable_outputs(&mut self);
|
fn enable_outputs(&mut self);
|
||||||
|
|
||||||
|
/// Set output compare mode.
|
||||||
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
||||||
let r = Self::regs_gp16();
|
let r = Self::regs_gp16();
|
||||||
let raw_channel: usize = channel.raw();
|
let raw_channel: usize = channel.index();
|
||||||
r.ccmr_output(raw_channel / 2)
|
r.ccmr_output(raw_channel / 2)
|
||||||
.modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
|
.modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set output polarity.
|
||||||
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||||
Self::regs_gp16()
|
Self::regs_gp16()
|
||||||
.ccer()
|
.ccer()
|
||||||
.modify(|w| w.set_ccp(channel.raw(), polarity.into()));
|
.modify(|w| w.set_ccp(channel.index(), polarity.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable/disable a channel.
|
||||||
fn enable_channel(&mut self, channel: Channel, enable: bool) {
|
fn enable_channel(&mut self, channel: Channel, enable: bool) {
|
||||||
Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.raw(), enable));
|
Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set compare value for a channel.
|
||||||
fn set_compare_value(&mut self, channel: Channel, value: u16) {
|
fn set_compare_value(&mut self, channel: Channel, value: u16) {
|
||||||
Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value));
|
Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get capture value for a channel.
|
||||||
fn get_capture_value(&mut self, channel: Channel) -> u16 {
|
fn get_capture_value(&mut self, channel: Channel) -> u16 {
|
||||||
Self::regs_gp16().ccr(channel.raw()).read().ccr()
|
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
|
||||||
fn get_max_compare_value(&self) -> u16 {
|
fn get_max_compare_value(&self) -> u16 {
|
||||||
Self::regs_gp16().arr().read().arr()
|
Self::regs_gp16().arr().read().arr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get compare value for a channel.
|
||||||
fn get_compare_value(&self, channel: Channel) -> u16 {
|
fn get_compare_value(&self, channel: Channel) -> u16 {
|
||||||
Self::regs_gp16().ccr(channel.raw()).read().ccr()
|
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capture/Compare 16-bit timer instance with complementary pin support.
|
||||||
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
|
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
|
||||||
|
/// Set complementary output polarity.
|
||||||
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||||
Self::regs_advanced()
|
Self::regs_advanced()
|
||||||
.ccer()
|
.ccer()
|
||||||
.modify(|w| w.set_ccnp(channel.raw(), polarity.into()));
|
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set clock divider for the dead time.
|
||||||
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
||||||
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
|
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set dead time, as a fraction of the max duty value.
|
||||||
fn set_dead_time_value(&mut self, value: u8) {
|
fn set_dead_time_value(&mut self, value: u8) {
|
||||||
Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
|
Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable/disable a complementary channel.
|
||||||
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
|
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
|
||||||
Self::regs_advanced()
|
Self::regs_advanced()
|
||||||
.ccer()
|
.ccer()
|
||||||
.modify(|w| w.set_ccne(channel.raw(), enable));
|
.modify(|w| w.set_ccne(channel.index(), enable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capture/Compare 32-bit timer instance.
|
||||||
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance {
|
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance {
|
||||||
|
/// Set comapre value for a channel.
|
||||||
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
||||||
Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value));
|
Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get capture value for a channel.
|
||||||
fn get_capture_value(&mut self, channel: Channel) -> u32 {
|
fn get_capture_value(&mut self, channel: Channel) -> u32 {
|
||||||
Self::regs_gp32().ccr(channel.raw()).read().ccr()
|
Self::regs_gp32().ccr(channel.index()).read().ccr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
|
||||||
fn get_max_compare_value(&self) -> u32 {
|
fn get_max_compare_value(&self) -> u32 {
|
||||||
Self::regs_gp32().arr().read().arr()
|
Self::regs_gp32().arr().read().arr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get compare value for a channel.
|
||||||
fn get_compare_value(&self, channel: Channel) -> u32 {
|
fn get_compare_value(&self, channel: Channel) -> u32 {
|
||||||
Self::regs_gp32().ccr(channel.raw()).read().ccr()
|
Self::regs_gp32().ccr(channel.index()).read().ccr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timer channel.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Channel {
|
pub enum Channel {
|
||||||
|
/// Channel 1.
|
||||||
Ch1,
|
Ch1,
|
||||||
|
/// Channel 2.
|
||||||
Ch2,
|
Ch2,
|
||||||
|
/// Channel 3.
|
||||||
Ch3,
|
Ch3,
|
||||||
|
/// Channel 4.
|
||||||
Ch4,
|
Ch4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
pub fn raw(&self) -> usize {
|
/// Get the channel index (0..3)
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Channel::Ch1 => 0,
|
Channel::Ch1 => 0,
|
||||||
Channel::Ch2 => 1,
|
Channel::Ch2 => 1,
|
||||||
@ -295,17 +366,25 @@ impl Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Input capture mode.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum InputCaptureMode {
|
pub enum InputCaptureMode {
|
||||||
|
/// Rising edge only.
|
||||||
Rising,
|
Rising,
|
||||||
|
/// Falling edge only.
|
||||||
Falling,
|
Falling,
|
||||||
|
/// Both rising or falling edges.
|
||||||
BothEdges,
|
BothEdges,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Input TI selection.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum InputTISelection {
|
pub enum InputTISelection {
|
||||||
|
/// Normal
|
||||||
Normal,
|
Normal,
|
||||||
|
/// Alternate
|
||||||
Alternate,
|
Alternate,
|
||||||
|
/// TRC
|
||||||
TRC,
|
TRC,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +398,7 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timer counting mode.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub enum CountingMode {
|
pub enum CountingMode {
|
||||||
@ -345,6 +425,7 @@ pub enum CountingMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CountingMode {
|
impl CountingMode {
|
||||||
|
/// Return whether this mode is edge-aligned (up or down).
|
||||||
pub fn is_edge_aligned(&self) -> bool {
|
pub fn is_edge_aligned(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
|
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
|
||||||
@ -352,6 +433,7 @@ impl CountingMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this mode is center-aligned.
|
||||||
pub fn is_center_aligned(&self) -> bool {
|
pub fn is_center_aligned(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
CountingMode::CenterAlignedDownInterrupts
|
CountingMode::CenterAlignedDownInterrupts
|
||||||
@ -386,16 +468,34 @@ impl From<(vals::Cms, vals::Dir)> for CountingMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Output compare mode.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OutputCompareMode {
|
pub enum OutputCompareMode {
|
||||||
|
/// The comparison between the output compare register TIMx_CCRx and
|
||||||
|
/// the counter TIMx_CNT has no effect on the outputs.
|
||||||
|
/// (this mode is used to generate a timing base).
|
||||||
Frozen,
|
Frozen,
|
||||||
|
/// Set channel to active level on match. OCxREF signal is forced high when the
|
||||||
|
/// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
|
||||||
ActiveOnMatch,
|
ActiveOnMatch,
|
||||||
|
/// Set channel to inactive level on match. OCxREF signal is forced low when the
|
||||||
|
/// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
|
||||||
InactiveOnMatch,
|
InactiveOnMatch,
|
||||||
|
/// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.
|
||||||
Toggle,
|
Toggle,
|
||||||
|
/// Force inactive level - OCxREF is forced low.
|
||||||
ForceInactive,
|
ForceInactive,
|
||||||
|
/// Force active level - OCxREF is forced high.
|
||||||
ForceActive,
|
ForceActive,
|
||||||
|
/// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx
|
||||||
|
/// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as
|
||||||
|
/// TIMx_CNT>TIMx_CCRx else active (OCxREF=1).
|
||||||
PwmMode1,
|
PwmMode1,
|
||||||
|
/// PWM mode 2 - In upcounting, channel is inactive as long as
|
||||||
|
/// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as
|
||||||
|
/// TIMx_CNT>TIMx_CCRx else inactive.
|
||||||
PwmMode2,
|
PwmMode2,
|
||||||
|
// TODO: there's more modes here depending on the chip family.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
|
impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
|
||||||
@ -413,9 +513,12 @@ impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timer output pin polarity.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OutputPolarity {
|
pub enum OutputPolarity {
|
||||||
|
/// Active high (higher duty value makes the pin spend more time high).
|
||||||
ActiveHigh,
|
ActiveHigh,
|
||||||
|
/// Active low (higher duty value makes the pin spend more time low).
|
||||||
ActiveLow,
|
ActiveLow,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,24 +531,31 @@ impl From<OutputPolarity> for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Basic 16-bit timer instance.
|
||||||
pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
|
pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
|
||||||
|
|
||||||
|
/// Gneral-purpose 16-bit timer instance.
|
||||||
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
|
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
|
||||||
|
|
||||||
|
/// Gneral-purpose 32-bit timer instance.
|
||||||
pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {}
|
pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {}
|
||||||
|
|
||||||
|
/// Advanced control timer instance.
|
||||||
pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {}
|
pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {}
|
||||||
|
|
||||||
|
/// Capture/Compare 16-bit timer instance.
|
||||||
pub trait CaptureCompare16bitInstance:
|
pub trait CaptureCompare16bitInstance:
|
||||||
sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static
|
sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capture/Compare 16-bit timer instance with complementary pin support.
|
||||||
pub trait ComplementaryCaptureCompare16bitInstance:
|
pub trait ComplementaryCaptureCompare16bitInstance:
|
||||||
sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static
|
sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Capture/Compare 32-bit timer instance.
|
||||||
pub trait CaptureCompare32bitInstance:
|
pub trait CaptureCompare32bitInstance:
|
||||||
sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static
|
sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static
|
||||||
{
|
{
|
||||||
|
@ -9,23 +9,30 @@ use crate::gpio::sealed::AFType;
|
|||||||
use crate::gpio::AnyPin;
|
use crate::gpio::AnyPin;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
/// Counting direction
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
|
/// Counting up.
|
||||||
Upcounting,
|
Upcounting,
|
||||||
|
/// Counting down.
|
||||||
Downcounting,
|
Downcounting,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ch1;
|
/// Channel 1 marker type.
|
||||||
pub struct Ch2;
|
pub enum Ch1 {}
|
||||||
|
/// Channel 2 marker type.
|
||||||
|
pub enum Ch2 {}
|
||||||
|
|
||||||
pub struct QeiPin<'d, Perip, Channel> {
|
/// Wrapper for using a pin with QEI.
|
||||||
|
pub struct QeiPin<'d, T, Channel> {
|
||||||
_pin: PeripheralRef<'d, AnyPin>,
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
phantom: PhantomData<(Perip, Channel)>,
|
phantom: PhantomData<(T, Channel)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! channel_impl {
|
macro_rules! channel_impl {
|
||||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||||
impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> {
|
impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> {
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self {
|
#[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")]
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
pin.set_low();
|
pin.set_low();
|
||||||
@ -45,11 +52,13 @@ macro_rules! channel_impl {
|
|||||||
channel_impl!(new_ch1, Ch1, Channel1Pin);
|
channel_impl!(new_ch1, Ch1, Channel1Pin);
|
||||||
channel_impl!(new_ch2, Ch2, Channel2Pin);
|
channel_impl!(new_ch2, Ch2, Channel2Pin);
|
||||||
|
|
||||||
|
/// Quadrature decoder driver.
|
||||||
pub struct Qei<'d, T> {
|
pub struct Qei<'d, T> {
|
||||||
_inner: PeripheralRef<'d, T>,
|
_inner: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
|
impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
|
||||||
|
/// Create a new quadrature decoder driver.
|
||||||
pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
|
pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
|
||||||
Self::new_inner(tim)
|
Self::new_inner(tim)
|
||||||
}
|
}
|
||||||
@ -84,6 +93,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
|
|||||||
Self { _inner: tim }
|
Self { _inner: tim }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get direction.
|
||||||
pub fn read_direction(&self) -> Direction {
|
pub fn read_direction(&self) -> Direction {
|
||||||
match T::regs_gp16().cr1().read().dir() {
|
match T::regs_gp16().cr1().read().dir() {
|
||||||
vals::Dir::DOWN => Direction::Downcounting,
|
vals::Dir::DOWN => Direction::Downcounting,
|
||||||
@ -91,6 +101,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get count.
|
||||||
pub fn count(&self) -> u16 {
|
pub fn count(&self) -> u16 {
|
||||||
T::regs_gp16().cnt().read().cnt()
|
T::regs_gp16().cnt().read().cnt()
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,28 @@ use crate::gpio::{AnyPin, OutputType};
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
pub struct Ch1;
|
/// Channel 1 marker type.
|
||||||
pub struct Ch2;
|
pub enum Ch1 {}
|
||||||
pub struct Ch3;
|
/// Channel 2 marker type.
|
||||||
pub struct Ch4;
|
pub enum Ch2 {}
|
||||||
|
/// Channel 3 marker type.
|
||||||
|
pub enum Ch3 {}
|
||||||
|
/// Channel 4 marker type.
|
||||||
|
pub enum Ch4 {}
|
||||||
|
|
||||||
pub struct PwmPin<'d, Perip, Channel> {
|
/// PWM pin wrapper.
|
||||||
|
///
|
||||||
|
/// This wraps a pin to make it usable with PWM.
|
||||||
|
pub struct PwmPin<'d, T, C> {
|
||||||
_pin: PeripheralRef<'d, AnyPin>,
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
phantom: PhantomData<(Perip, Channel)>,
|
phantom: PhantomData<(T, C)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! channel_impl {
|
macro_rules! channel_impl {
|
||||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||||
impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> {
|
impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> {
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self {
|
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
pin.set_low();
|
pin.set_low();
|
||||||
@ -46,11 +54,13 @@ channel_impl!(new_ch2, Ch2, Channel2Pin);
|
|||||||
channel_impl!(new_ch3, Ch3, Channel3Pin);
|
channel_impl!(new_ch3, Ch3, Channel3Pin);
|
||||||
channel_impl!(new_ch4, Ch4, Channel4Pin);
|
channel_impl!(new_ch4, Ch4, Channel4Pin);
|
||||||
|
|
||||||
|
/// Simple PWM driver.
|
||||||
pub struct SimplePwm<'d, T> {
|
pub struct SimplePwm<'d, T> {
|
||||||
inner: PeripheralRef<'d, T>,
|
inner: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||||
|
/// Create a new simple PWM driver.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tim: impl Peripheral<P = T> + 'd,
|
tim: impl Peripheral<P = T> + 'd,
|
||||||
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
_ch1: Option<PwmPin<'d, T, Ch1>>,
|
||||||
@ -71,7 +81,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
let mut this = Self { inner: tim };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.inner.set_counting_mode(counting_mode);
|
this.inner.set_counting_mode(counting_mode);
|
||||||
this.set_freq(freq);
|
this.set_frequency(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
this.inner.enable_outputs();
|
this.inner.enable_outputs();
|
||||||
@ -87,15 +97,21 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable the given channel.
|
||||||
pub fn enable(&mut self, channel: Channel) {
|
pub fn enable(&mut self, channel: Channel) {
|
||||||
self.inner.enable_channel(channel, true);
|
self.inner.enable_channel(channel, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable the given channel.
|
||||||
pub fn disable(&mut self, channel: Channel) {
|
pub fn disable(&mut self, channel: Channel) {
|
||||||
self.inner.enable_channel(channel, false);
|
self.inner.enable_channel(channel, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_freq(&mut self, freq: Hertz) {
|
/// Set PWM frequency.
|
||||||
|
///
|
||||||
|
/// Note: when you call this, the max duty value changes, so you will have to
|
||||||
|
/// call `set_duty` on all channels with the duty calculated based on the new max duty.
|
||||||
|
pub fn set_frequency(&mut self, freq: Hertz) {
|
||||||
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
||||||
2u8
|
2u8
|
||||||
} else {
|
} else {
|
||||||
@ -104,15 +120,22 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
self.inner.set_frequency(freq * multiplier);
|
self.inner.set_frequency(freq * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get max duty value.
|
||||||
|
///
|
||||||
|
/// This value depends on the configured frequency and the timer's clock rate from RCC.
|
||||||
pub fn get_max_duty(&self) -> u16 {
|
pub fn get_max_duty(&self) -> u16 {
|
||||||
self.inner.get_max_compare_value() + 1
|
self.inner.get_max_compare_value() + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the duty for a given channel.
|
||||||
|
///
|
||||||
|
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
|
||||||
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
|
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
|
||||||
assert!(duty <= self.get_max_duty());
|
assert!(duty <= self.get_max_duty());
|
||||||
self.inner.set_compare_value(channel, duty)
|
self.inner.set_compare_value(channel, duty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the output polarity for a given channel.
|
||||||
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||||
self.inner.set_output_polarity(channel, polarity);
|
self.inner.set_output_polarity(channel, polarity);
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ impl<T: BasicInstance> interrupt::typelevel::Handler<T::Interrupt> for Interrupt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Buffered UART State
|
||||||
pub struct State {
|
pub struct State {
|
||||||
rx_waker: AtomicWaker,
|
rx_waker: AtomicWaker,
|
||||||
rx_buf: RingBuffer,
|
rx_buf: RingBuffer,
|
||||||
@ -91,6 +92,7 @@ pub struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
/// Create new state
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rx_buf: RingBuffer::new(),
|
rx_buf: RingBuffer::new(),
|
||||||
@ -101,15 +103,18 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bidirectional buffered UART
|
||||||
pub struct BufferedUart<'d, T: BasicInstance> {
|
pub struct BufferedUart<'d, T: BasicInstance> {
|
||||||
rx: BufferedUartRx<'d, T>,
|
rx: BufferedUartRx<'d, T>,
|
||||||
tx: BufferedUartTx<'d, T>,
|
tx: BufferedUartTx<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tx-only buffered UART
|
||||||
pub struct BufferedUartTx<'d, T: BasicInstance> {
|
pub struct BufferedUartTx<'d, T: BasicInstance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rx-only buffered UART
|
||||||
pub struct BufferedUartRx<'d, T: BasicInstance> {
|
pub struct BufferedUartRx<'d, T: BasicInstance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
}
|
}
|
||||||
@ -142,6 +147,7 @@ impl<'d, T: BasicInstance> SetConfig for BufferedUartTx<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||||
|
/// Create a new bidirectional buffered UART driver
|
||||||
pub fn new(
|
pub fn new(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -158,6 +164,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
|||||||
Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
|
Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins
|
||||||
pub fn new_with_rtscts(
|
pub fn new_with_rtscts(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -185,6 +192,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
|||||||
Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
|
Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new bidirectional buffered UART driver with a driver-enable pin
|
||||||
#[cfg(not(any(usart_v1, usart_v2)))]
|
#[cfg(not(any(usart_v1, usart_v2)))]
|
||||||
pub fn new_with_de(
|
pub fn new_with_de(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
@ -246,10 +254,12 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split the driver into a Tx and Rx part (useful for sending to separate tasks)
|
||||||
pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
|
pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
|
||||||
(self.tx, self.rx)
|
(self.tx, self.rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)?;
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
@ -337,6 +347,7 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)?;
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
@ -418,6 +429,7 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)?;
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Universal Synchronous/Asynchronous Receiver Transmitter (USART, UART, LPUART)
|
//! Universal Synchronous/Asynchronous Receiver Transmitter (USART, UART, LPUART)
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
@ -77,21 +78,29 @@ impl<T: BasicInstance> interrupt::typelevel::Handler<T::Interrupt> for Interrupt
|
|||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Number of data bits
|
||||||
pub enum DataBits {
|
pub enum DataBits {
|
||||||
|
/// 8 Data Bits
|
||||||
DataBits8,
|
DataBits8,
|
||||||
|
/// 9 Data Bits
|
||||||
DataBits9,
|
DataBits9,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Parity
|
||||||
pub enum Parity {
|
pub enum Parity {
|
||||||
|
/// No parity
|
||||||
ParityNone,
|
ParityNone,
|
||||||
|
/// Even Parity
|
||||||
ParityEven,
|
ParityEven,
|
||||||
|
/// Odd Parity
|
||||||
ParityOdd,
|
ParityOdd,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Number of stop bits
|
||||||
pub enum StopBits {
|
pub enum StopBits {
|
||||||
#[doc = "1 stop bit"]
|
#[doc = "1 stop bit"]
|
||||||
STOP1,
|
STOP1,
|
||||||
@ -106,26 +115,37 @@ pub enum StopBits {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Config Error
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
|
/// Baudrate too low
|
||||||
BaudrateTooLow,
|
BaudrateTooLow,
|
||||||
|
/// Baudrate too high
|
||||||
BaudrateTooHigh,
|
BaudrateTooHigh,
|
||||||
|
/// Rx or Tx not enabled
|
||||||
RxOrTxNotEnabled,
|
RxOrTxNotEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
/// Config
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// Baud rate
|
||||||
pub baudrate: u32,
|
pub baudrate: u32,
|
||||||
|
/// Number of data bits
|
||||||
pub data_bits: DataBits,
|
pub data_bits: DataBits,
|
||||||
|
/// Number of stop bits
|
||||||
pub stop_bits: StopBits,
|
pub stop_bits: StopBits,
|
||||||
|
/// Parity type
|
||||||
pub parity: Parity,
|
pub parity: Parity,
|
||||||
/// if true, on read-like method, if there is a latent error pending,
|
|
||||||
/// read will abort, the error reported and cleared
|
/// If true: on a read-like method, if there is a latent error pending,
|
||||||
/// if false, the error is ignored and cleared
|
/// the read will abort and the error will be reported and cleared
|
||||||
|
///
|
||||||
|
/// If false: the error is ignored and cleared
|
||||||
pub detect_previous_overrun: bool,
|
pub detect_previous_overrun: bool,
|
||||||
|
|
||||||
/// Set this to true if the line is considered noise free.
|
/// Set this to true if the line is considered noise free.
|
||||||
/// This will increase the receivers tolerance to clock deviations,
|
/// This will increase the receiver’s tolerance to clock deviations,
|
||||||
/// but will effectively disable noise detection.
|
/// but will effectively disable noise detection.
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
pub assume_noise_free: bool,
|
pub assume_noise_free: bool,
|
||||||
@ -188,6 +208,7 @@ enum ReadCompletionEvent {
|
|||||||
Idle(usize),
|
Idle(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bidirectional UART Driver
|
||||||
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
||||||
tx: UartTx<'d, T, TxDma>,
|
tx: UartTx<'d, T, TxDma>,
|
||||||
rx: UartRx<'d, T, RxDma>,
|
rx: UartRx<'d, T, RxDma>,
|
||||||
@ -203,6 +224,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tx-only UART Driver
|
||||||
pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
|
pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
tx_dma: PeripheralRef<'d, TxDma>,
|
tx_dma: PeripheralRef<'d, TxDma>,
|
||||||
@ -217,6 +239,7 @@ impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rx-only UART Driver
|
||||||
pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
|
pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
rx_dma: PeripheralRef<'d, RxDma>,
|
rx_dma: PeripheralRef<'d, RxDma>,
|
||||||
@ -247,6 +270,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
|||||||
Self::new_inner(peri, tx, tx_dma, config)
|
Self::new_inner(peri, tx, tx_dma, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new tx-only UART with a clear-to-send pin
|
||||||
pub fn new_with_cts(
|
pub fn new_with_cts(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||||
@ -288,10 +312,12 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an asynchronous UART write
|
||||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error>
|
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
TxDma: crate::usart::TxDma<T>,
|
TxDma: crate::usart::TxDma<T>,
|
||||||
@ -308,6 +334,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking UART write
|
||||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
for &b in buffer {
|
for &b in buffer {
|
||||||
@ -317,6 +344,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Block until transmission complete
|
||||||
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
while !sr(r).read().tc() {}
|
while !sr(r).read().tc() {}
|
||||||
@ -338,6 +366,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
Self::new_inner(peri, rx, rx_dma, config)
|
Self::new_inner(peri, rx, rx_dma, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new rx-only UART with a request-to-send pin
|
||||||
pub fn new_with_rts(
|
pub fn new_with_rts(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -387,6 +416,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)
|
||||||
}
|
}
|
||||||
@ -444,6 +474,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
Ok(sr.rxne())
|
Ok(sr.rxne())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an asynchronous UART read
|
||||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error>
|
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RxDma: crate::usart::RxDma<T>,
|
RxDma: crate::usart::RxDma<T>,
|
||||||
@ -453,6 +484,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a single u8 if there is one available, otherwise return WouldBlock
|
||||||
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
|
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
if self.check_rx_flags()? {
|
if self.check_rx_flags()? {
|
||||||
@ -462,6 +494,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking read into `buffer`
|
||||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
for b in buffer {
|
for b in buffer {
|
||||||
@ -471,6 +504,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an asynchronous read with idle line detection enabled
|
||||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
||||||
where
|
where
|
||||||
RxDma: crate::usart::RxDma<T>,
|
RxDma: crate::usart::RxDma<T>,
|
||||||
@ -695,6 +729,7 @@ impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||||
|
/// Create a new bidirectional UART
|
||||||
pub fn new(
|
pub fn new(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||||
@ -711,6 +746,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
|
Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new bidirectional UART with request-to-send and clear-to-send pins
|
||||||
pub fn new_with_rtscts(
|
pub fn new_with_rtscts(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||||
@ -738,6 +774,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(usart_v1, usart_v2)))]
|
#[cfg(not(any(usart_v1, usart_v2)))]
|
||||||
|
/// Create a new bidirectional UART with a driver-enable pin
|
||||||
pub fn new_with_de(
|
pub fn new_with_de(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||||
@ -813,6 +850,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an asynchronous write
|
||||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error>
|
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
TxDma: crate::usart::TxDma<T>,
|
TxDma: crate::usart::TxDma<T>,
|
||||||
@ -820,14 +858,17 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
self.tx.write(buffer).await
|
self.tx.write(buffer).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking write
|
||||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
self.tx.blocking_write(buffer)
|
self.tx.blocking_write(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Block until transmission complete
|
||||||
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
||||||
self.tx.blocking_flush()
|
self.tx.blocking_flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an asynchronous read into `buffer`
|
||||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error>
|
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RxDma: crate::usart::RxDma<T>,
|
RxDma: crate::usart::RxDma<T>,
|
||||||
@ -835,14 +876,17 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
self.rx.read(buffer).await
|
self.rx.read(buffer).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a single `u8` or return `WouldBlock`
|
||||||
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
|
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
|
||||||
self.rx.nb_read()
|
self.rx.nb_read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking read into `buffer`
|
||||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
self.rx.blocking_read(buffer)
|
self.rx.blocking_read(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initiate an an asynchronous read with idle line detection enabled
|
||||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error>
|
||||||
where
|
where
|
||||||
RxDma: crate::usart::RxDma<T>,
|
RxDma: crate::usart::RxDma<T>,
|
||||||
@ -1292,8 +1336,10 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Basic UART driver instance
|
||||||
pub trait BasicInstance: Peripheral<P = Self> + sealed::BasicInstance + 'static + Send {}
|
pub trait BasicInstance: Peripheral<P = Self> + sealed::BasicInstance + 'static + Send {}
|
||||||
|
|
||||||
|
/// Full UART driver instance
|
||||||
pub trait FullInstance: sealed::FullInstance {}
|
pub trait FullInstance: sealed::FullInstance {}
|
||||||
|
|
||||||
pin_trait!(RxPin, BasicInstance);
|
pin_trait!(RxPin, BasicInstance);
|
||||||
|
@ -11,6 +11,7 @@ use super::{clear_interrupt_flags, rdr, reconfigure, sr, BasicInstance, Config,
|
|||||||
use crate::dma::ReadableRingBuffer;
|
use crate::dma::ReadableRingBuffer;
|
||||||
use crate::usart::{Regs, Sr};
|
use crate::usart::{Regs, Sr};
|
||||||
|
|
||||||
|
/// Rx-only Ring-buffered UART Driver
|
||||||
pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> {
|
pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
ring_buf: ReadableRingBuffer<'d, RxDma, u8>,
|
ring_buf: ReadableRingBuffer<'d, RxDma, u8>,
|
||||||
@ -27,8 +28,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> SetConfig for RingBufferedUar
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
|
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
|
||||||
/// Turn the `UartRx` into a buffered uart which can continously receive in the background
|
/// Turn the `UartRx` into a buffered uart which can continously receive in the background
|
||||||
/// without the possibility of loosing bytes. The `dma_buf` is a buffer registered to the
|
/// without the possibility of losing bytes. The `dma_buf` is a buffer registered to the
|
||||||
/// DMA controller, and must be sufficiently large, such that it will not overflow.
|
/// DMA controller, and must be large enough to prevent overflows.
|
||||||
pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T, RxDma> {
|
pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T, RxDma> {
|
||||||
assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
|
assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxDma> {
|
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxDma> {
|
||||||
|
/// Clear the ring buffer and start receiving in the background
|
||||||
pub fn start(&mut self) -> Result<(), Error> {
|
pub fn start(&mut self) -> Result<(), Error> {
|
||||||
// Clear the ring buffer so that it is ready to receive data
|
// Clear the ring buffer so that it is ready to receive data
|
||||||
self.ring_buf.clear();
|
self.ring_buf.clear();
|
||||||
@ -64,6 +66,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
|
|||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cleanly stop and reconfigure the driver
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
self.teardown_uart();
|
self.teardown_uart();
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Universal Serial Bus (USB)
|
||||||
|
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
|
|
||||||
@ -10,7 +12,9 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB instance trait.
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {
|
pub trait Instance: sealed::Instance + RccPeripheral + 'static {
|
||||||
|
/// Interrupt for this USB instance.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +244,7 @@ struct EndpointData {
|
|||||||
used_out: bool,
|
used_out: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB driver.
|
||||||
pub struct Driver<'d, T: Instance> {
|
pub struct Driver<'d, T: Instance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
alloc: [EndpointData; EP_COUNT],
|
alloc: [EndpointData; EP_COUNT],
|
||||||
@ -251,6 +252,7 @@ pub struct Driver<'d, T: Instance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Driver<'d, T> {
|
impl<'d, T: Instance> Driver<'d, T> {
|
||||||
|
/// Create a new USB driver.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
_usb: impl Peripheral<P = T> + 'd,
|
_usb: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
@ -465,6 +467,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB bus.
|
||||||
pub struct Bus<'d, T: Instance> {
|
pub struct Bus<'d, T: Instance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
ep_types: [EpType; EP_COUNT - 1],
|
ep_types: [EpType; EP_COUNT - 1],
|
||||||
@ -640,6 +643,7 @@ trait Dir {
|
|||||||
fn waker(i: usize) -> &'static AtomicWaker;
|
fn waker(i: usize) -> &'static AtomicWaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker type for the "IN" direction.
|
||||||
pub enum In {}
|
pub enum In {}
|
||||||
impl Dir for In {
|
impl Dir for In {
|
||||||
fn dir() -> Direction {
|
fn dir() -> Direction {
|
||||||
@ -652,6 +656,7 @@ impl Dir for In {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker type for the "OUT" direction.
|
||||||
pub enum Out {}
|
pub enum Out {}
|
||||||
impl Dir for Out {
|
impl Dir for Out {
|
||||||
fn dir() -> Direction {
|
fn dir() -> Direction {
|
||||||
@ -664,6 +669,7 @@ impl Dir for Out {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB endpoint.
|
||||||
pub struct Endpoint<'d, T: Instance, D> {
|
pub struct Endpoint<'d, T: Instance, D> {
|
||||||
_phantom: PhantomData<(&'d mut T, D)>,
|
_phantom: PhantomData<(&'d mut T, D)>,
|
||||||
info: EndpointInfo,
|
info: EndpointInfo,
|
||||||
@ -813,6 +819,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB control pipe.
|
||||||
pub struct ControlPipe<'d, T: Instance> {
|
pub struct ControlPipe<'d, T: Instance> {
|
||||||
_phantom: PhantomData<&'d mut T>,
|
_phantom: PhantomData<&'d mut T>,
|
||||||
max_packet_size: u16,
|
max_packet_size: u16,
|
||||||
|
@ -20,7 +20,9 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB OTG instance.
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral {
|
pub trait Instance: sealed::Instance + RccPeripheral {
|
||||||
|
/// Interrupt for this USB OTG instance.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +204,7 @@ pub enum PhyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PhyType {
|
impl PhyType {
|
||||||
|
/// Get whether this PHY is any of the internal types.
|
||||||
pub fn internal(&self) -> bool {
|
pub fn internal(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true,
|
PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true,
|
||||||
@ -211,6 +212,7 @@ impl PhyType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether this PHY is any of the high-speed types.
|
||||||
pub fn high_speed(&self) -> bool {
|
pub fn high_speed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
PhyType::InternalFullSpeed => false,
|
PhyType::InternalFullSpeed => false,
|
||||||
@ -218,7 +220,7 @@ impl PhyType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_dspd(&self) -> vals::Dspd {
|
fn to_dspd(&self) -> vals::Dspd {
|
||||||
match self {
|
match self {
|
||||||
PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL,
|
PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL,
|
||||||
PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED,
|
PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED,
|
||||||
@ -230,6 +232,7 @@ impl PhyType {
|
|||||||
/// Indicates that [State::ep_out_buffers] is empty.
|
/// Indicates that [State::ep_out_buffers] is empty.
|
||||||
const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX;
|
const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX;
|
||||||
|
|
||||||
|
/// USB OTG driver state.
|
||||||
pub struct State<const EP_COUNT: usize> {
|
pub struct State<const EP_COUNT: usize> {
|
||||||
/// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true.
|
/// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true.
|
||||||
ep0_setup_data: UnsafeCell<[u8; 8]>,
|
ep0_setup_data: UnsafeCell<[u8; 8]>,
|
||||||
@ -247,6 +250,7 @@ unsafe impl<const EP_COUNT: usize> Send for State<EP_COUNT> {}
|
|||||||
unsafe impl<const EP_COUNT: usize> Sync for State<EP_COUNT> {}
|
unsafe impl<const EP_COUNT: usize> Sync for State<EP_COUNT> {}
|
||||||
|
|
||||||
impl<const EP_COUNT: usize> State<EP_COUNT> {
|
impl<const EP_COUNT: usize> State<EP_COUNT> {
|
||||||
|
/// Create a new State.
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||||
const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _);
|
const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _);
|
||||||
@ -271,6 +275,7 @@ struct EndpointData {
|
|||||||
fifo_size_words: u16,
|
fifo_size_words: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB driver config.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -297,6 +302,7 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB driver.
|
||||||
pub struct Driver<'d, T: Instance> {
|
pub struct Driver<'d, T: Instance> {
|
||||||
config: Config,
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
@ -527,6 +533,7 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB bus.
|
||||||
pub struct Bus<'d, T: Instance> {
|
pub struct Bus<'d, T: Instance> {
|
||||||
config: Config,
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
@ -1092,6 +1099,7 @@ trait Dir {
|
|||||||
fn dir() -> Direction;
|
fn dir() -> Direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker type for the "IN" direction.
|
||||||
pub enum In {}
|
pub enum In {}
|
||||||
impl Dir for In {
|
impl Dir for In {
|
||||||
fn dir() -> Direction {
|
fn dir() -> Direction {
|
||||||
@ -1099,6 +1107,7 @@ impl Dir for In {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker type for the "OUT" direction.
|
||||||
pub enum Out {}
|
pub enum Out {}
|
||||||
impl Dir for Out {
|
impl Dir for Out {
|
||||||
fn dir() -> Direction {
|
fn dir() -> Direction {
|
||||||
@ -1106,6 +1115,7 @@ impl Dir for Out {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB endpoint.
|
||||||
pub struct Endpoint<'d, T: Instance, D> {
|
pub struct Endpoint<'d, T: Instance, D> {
|
||||||
_phantom: PhantomData<(&'d mut T, D)>,
|
_phantom: PhantomData<(&'d mut T, D)>,
|
||||||
info: EndpointInfo,
|
info: EndpointInfo,
|
||||||
@ -1299,6 +1309,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB control pipe.
|
||||||
pub struct ControlPipe<'d, T: Instance> {
|
pub struct ControlPipe<'d, T: Instance> {
|
||||||
_phantom: PhantomData<&'d mut T>,
|
_phantom: PhantomData<&'d mut T>,
|
||||||
max_packet_size: u16,
|
max_packet_size: u16,
|
||||||
|
@ -6,6 +6,7 @@ use stm32_metapac::iwdg::vals::{Key, Pr};
|
|||||||
|
|
||||||
use crate::rcc::LSI_FREQ;
|
use crate::rcc::LSI_FREQ;
|
||||||
|
|
||||||
|
/// Independent watchdog (IWDG) driver.
|
||||||
pub struct IndependentWatchdog<'d, T: Instance> {
|
pub struct IndependentWatchdog<'d, T: Instance> {
|
||||||
wdg: PhantomData<&'d mut T>,
|
wdg: PhantomData<&'d mut T>,
|
||||||
}
|
}
|
||||||
@ -64,10 +65,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
|||||||
IndependentWatchdog { wdg: PhantomData }
|
IndependentWatchdog { wdg: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unleash (start) the watchdog.
|
||||||
pub fn unleash(&mut self) {
|
pub fn unleash(&mut self) {
|
||||||
T::regs().kr().write(|w| w.set_key(Key::START));
|
T::regs().kr().write(|w| w.set_key(Key::START));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pet (reload, refresh) the watchdog.
|
||||||
pub fn pet(&mut self) {
|
pub fn pet(&mut self) {
|
||||||
T::regs().kr().write(|w| w.set_key(Key::RESET));
|
T::regs().kr().write(|w| w.set_key(Key::RESET));
|
||||||
}
|
}
|
||||||
@ -79,6 +82,7 @@ mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// IWDG instance trait.
|
||||||
pub trait Instance: sealed::Instance {}
|
pub trait Instance: sealed::Instance {}
|
||||||
|
|
||||||
foreach_peripheral!(
|
foreach_peripheral!(
|
||||||
|
20
embassy-usb-dfu/README.md
Normal file
20
embassy-usb-dfu/README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# embassy-usb-dfu
|
||||||
|
|
||||||
|
An implementation of the USB DFU 1.1 protocol using embassy-boot. It has 2 components depending on which feature is enabled by the user.
|
||||||
|
|
||||||
|
* DFU protocol mode, enabled by the `dfu` feature. This mode corresponds to the transfer phase DFU protocol described by the USB IF. It supports DFU_DNLOAD requests if marked by the user, and will automatically reset the chip once a DFU transaction has been completed. It also responds to DFU_GETSTATUS, DFU_GETSTATE, DFU_ABORT, and DFU_CLRSTATUS with no user intervention.
|
||||||
|
* DFU runtime mode, enabled by the `application feature`. This mode allows users to expose a DFU interface on their USB device, informing the host of the capability to DFU over USB, and allowing the host to reset the device into its bootloader to complete a DFU operation. Supports DFU_GETSTATUS and DFU_DETACH. When detach/reset is seen by the device as described by the standard, will write a new DFU magic number into the bootloader state in flash, and reset the system.
|
||||||
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
@ -24,6 +24,7 @@ pub struct Control<'d, STATE: NorFlash, RST: Reset> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> {
|
impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> {
|
||||||
|
/// Create a new DFU instance to expose a DFU interface.
|
||||||
pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
|
pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
Control {
|
Control {
|
||||||
firmware_state,
|
firmware_state,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
//! USB DFU constants.
|
||||||
pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE;
|
pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE;
|
||||||
pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01;
|
pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01;
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@ -18,10 +19,15 @@ defmt::bitflags! {
|
|||||||
|
|
||||||
#[cfg(not(feature = "defmt"))]
|
#[cfg(not(feature = "defmt"))]
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
|
/// Attributes supported by the DFU controller.
|
||||||
pub struct DfuAttributes: u8 {
|
pub struct DfuAttributes: u8 {
|
||||||
|
/// Generate WillDetache sequence on bus.
|
||||||
const WILL_DETACH = 0b0000_1000;
|
const WILL_DETACH = 0b0000_1000;
|
||||||
|
/// Device can communicate during manifestation phase.
|
||||||
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||||
|
/// Capable of upload.
|
||||||
const CAN_UPLOAD = 0b0000_0010;
|
const CAN_UPLOAD = 0b0000_0010;
|
||||||
|
/// Capable of download.
|
||||||
const CAN_DOWNLOAD = 0b0000_0001;
|
const CAN_DOWNLOAD = 0b0000_0001;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,7 +35,7 @@ bitflags::bitflags! {
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub enum State {
|
pub(crate) enum State {
|
||||||
AppIdle = 0,
|
AppIdle = 0,
|
||||||
AppDetach = 1,
|
AppDetach = 1,
|
||||||
DfuIdle = 2,
|
DfuIdle = 2,
|
||||||
@ -46,7 +52,7 @@ pub enum State {
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub enum Status {
|
pub(crate) enum Status {
|
||||||
Ok = 0x00,
|
Ok = 0x00,
|
||||||
ErrTarget = 0x01,
|
ErrTarget = 0x01,
|
||||||
ErrFile = 0x02,
|
ErrFile = 0x02,
|
||||||
@ -67,7 +73,7 @@ pub enum Status {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Request {
|
pub(crate) enum Request {
|
||||||
Detach = 0,
|
Detach = 0,
|
||||||
Dnload = 1,
|
Dnload = 1,
|
||||||
Upload = 2,
|
Upload = 2,
|
||||||
|
@ -23,6 +23,7 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> {
|
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> {
|
||||||
|
/// Create a new DFU instance to handle DFU transfers.
|
||||||
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self {
|
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
updater,
|
updater,
|
@ -1,12 +1,14 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs)]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
|
|
||||||
#[cfg(feature = "dfu")]
|
#[cfg(feature = "dfu")]
|
||||||
mod bootloader;
|
mod dfu;
|
||||||
#[cfg(feature = "dfu")]
|
#[cfg(feature = "dfu")]
|
||||||
pub use self::bootloader::*;
|
pub use self::dfu::*;
|
||||||
|
|
||||||
#[cfg(feature = "application")]
|
#[cfg(feature = "application")]
|
||||||
mod application;
|
mod application;
|
||||||
@ -17,7 +19,7 @@ pub use self::application::*;
|
|||||||
all(feature = "dfu", feature = "application"),
|
all(feature = "dfu", feature = "application"),
|
||||||
not(any(feature = "dfu", feature = "application"))
|
not(any(feature = "dfu", feature = "application"))
|
||||||
))]
|
))]
|
||||||
compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features");
|
compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `application` features");
|
||||||
|
|
||||||
/// Provides a platform-agnostic interface for initiating a system reset.
|
/// Provides a platform-agnostic interface for initiating a system reset.
|
||||||
///
|
///
|
||||||
@ -26,9 +28,11 @@ compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `a
|
|||||||
///
|
///
|
||||||
/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function.
|
/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function.
|
||||||
pub trait Reset {
|
pub trait Reset {
|
||||||
|
/// Reset the device.
|
||||||
fn sys_reset() -> !;
|
fn sys_reset() -> !;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset immediately.
|
||||||
#[cfg(feature = "esp32c3-hal")]
|
#[cfg(feature = "esp32c3-hal")]
|
||||||
pub struct ResetImmediate;
|
pub struct ResetImmediate;
|
||||||
|
|
||||||
@ -40,6 +44,7 @@ impl Reset for ResetImmediate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset immediately.
|
||||||
#[cfg(feature = "cortex-m")]
|
#[cfg(feature = "cortex-m")]
|
||||||
pub struct ResetImmediate;
|
pub struct ResetImmediate;
|
||||||
|
|
||||||
|
@ -13,3 +13,17 @@ async fn logger_task(driver: Driver<'static, USB>) {
|
|||||||
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
@ -110,7 +110,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
&mut dp.DMA1_CH2,
|
&mut dp.DMA1_CH2,
|
||||||
5,
|
5,
|
||||||
color_list[color_list_index],
|
color_list[color_list_index],
|
||||||
pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _,
|
pac::TIM3.ccr(pwm_channel.index()).as_ptr() as *mut _,
|
||||||
dma_transfer_option,
|
dma_transfer_option,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -85,7 +85,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
|
|||||||
|
|
||||||
let mut this = Self { inner: tim };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.set_freq(freq);
|
this.set_frequency(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
let r = T::regs_gp32();
|
let r = T::regs_gp32();
|
||||||
@ -102,14 +102,14 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable(&mut self, channel: Channel) {
|
pub fn enable(&mut self, channel: Channel) {
|
||||||
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true));
|
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable(&mut self, channel: Channel) {
|
pub fn disable(&mut self, channel: Channel) {
|
||||||
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false));
|
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_freq(&mut self, freq: Hertz) {
|
pub fn set_frequency(&mut self, freq: Hertz) {
|
||||||
<T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq);
|
<T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +119,6 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
|
|||||||
|
|
||||||
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
|
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
|
||||||
defmt::assert!(duty < self.get_max_duty());
|
defmt::assert!(duty < self.get_max_duty());
|
||||||
T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty))
|
T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user