mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Merge branch 'embassy-rs:main' into main
This commit is contained in:
commit
7609313a75
9
ci.sh
9
ci.sh
@ -88,6 +88,13 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \
|
||||
@ -223,6 +230,8 @@ cargo batch \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --out-dir out/tests/stm32f091rc \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \
|
||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||
--- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \
|
||||
|
@ -242,13 +242,12 @@ where
|
||||
cmd,
|
||||
iface,
|
||||
}) => {
|
||||
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await;
|
||||
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;
|
||||
self.check_status(&mut buf).await;
|
||||
}
|
||||
Either3::Second(packet) => {
|
||||
trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
||||
|
||||
let mut buf = [0; 512];
|
||||
let buf8 = slice8_mut(&mut buf);
|
||||
|
||||
// There MUST be 2 bytes of padding between the SDPCM and BDC headers.
|
||||
@ -480,9 +479,8 @@ where
|
||||
self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
|
||||
}
|
||||
|
||||
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) {
|
||||
let mut buf = [0; 512];
|
||||
let buf8 = slice8_mut(&mut buf);
|
||||
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
|
||||
let buf8 = slice8_mut(buf);
|
||||
|
||||
let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
|
||||
|
||||
|
@ -118,6 +118,13 @@ features = [
|
||||
]
|
||||
----
|
||||
|
||||
If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
use embassy_stm32 as _;
|
||||
----
|
||||
|
||||
== Error: `Only one package in the dependency graph may specify the same links value.`
|
||||
|
||||
You have multiple versions of the same crate in your dependency tree. This means that some of your
|
||||
|
@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
|
||||
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
dfu: DFU,
|
||||
state: FirmwareState<'d, STATE>,
|
||||
last_erased_dfu_sector_index: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
@ -56,6 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
Self {
|
||||
dfu: config.dfu,
|
||||
state: FirmwareState::new(config.state, aligned),
|
||||
last_erased_dfu_sector_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +74,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
/// proceed with updating the firmware as it must be signed with a
|
||||
/// corresponding private key (otherwise it could be malicious firmware).
|
||||
///
|
||||
/// Mark to trigger firmware swap on next boot if verify suceeds.
|
||||
/// Mark to trigger firmware swap on next boot if verify succeeds.
|
||||
///
|
||||
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
|
||||
/// been generated from a SHA-512 digest of the firmware bytes.
|
||||
@ -172,21 +174,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
self.state.mark_booted().await
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
/// Writes firmware data to the device.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
/// This function writes the given data to the firmware area starting at the specified offset.
|
||||
/// It handles sector erasures and data writes while verifying the device is in a proper state
|
||||
/// for firmware updates. The function ensures that only unerased sectors are erased before
|
||||
/// writing and efficiently handles the writing process across sector boundaries and in
|
||||
/// various configurations (data size, sector size, etc.).
|
||||
///
|
||||
/// # Safety
|
||||
/// # Arguments
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
/// * `offset` - The starting offset within the firmware area where data writing should begin.
|
||||
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
|
||||
/// multiple of NorFlash WRITE_SIZE.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
///
|
||||
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
|
||||
/// - There is a failure erasing a sector before writing.
|
||||
/// - There is a failure writing data to the device.
|
||||
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||
|
||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||
self.state.verify_booted().await?;
|
||||
|
||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
|
||||
// Initialize variables to keep track of the remaining data and the current offset.
|
||||
let mut remaining_data = data;
|
||||
let mut offset = offset;
|
||||
|
||||
self.dfu.write(offset as u32, data).await?;
|
||||
// Continue writing as long as there is data left to write.
|
||||
while !remaining_data.is_empty() {
|
||||
// Compute the current sector and its boundaries.
|
||||
let current_sector = offset / DFU::ERASE_SIZE;
|
||||
let sector_start = current_sector * DFU::ERASE_SIZE;
|
||||
let sector_end = sector_start + DFU::ERASE_SIZE;
|
||||
// Determine if the current sector needs to be erased before writing.
|
||||
let need_erase = self
|
||||
.last_erased_dfu_sector_index
|
||||
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
|
||||
|
||||
// If the sector needs to be erased, erase it and update the last erased sector index.
|
||||
if need_erase {
|
||||
self.dfu.erase(sector_start as u32, sector_end as u32).await?;
|
||||
self.last_erased_dfu_sector_index = Some(current_sector);
|
||||
}
|
||||
|
||||
// Calculate the size of the data chunk that can be written in the current iteration.
|
||||
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
|
||||
// Split the data to get the current chunk to be written and the remaining data.
|
||||
let (data_chunk, rest) = remaining_data.split_at(write_size);
|
||||
|
||||
// Write the current data chunk.
|
||||
self.dfu.write(offset as u32, data_chunk).await?;
|
||||
|
||||
// Update the offset and remaining data for the next iteration.
|
||||
remaining_data = rest;
|
||||
offset += write_size;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -276,16 +325,25 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.read(0, &mut self.aligned).await?;
|
||||
|
||||
if self.aligned.iter().any(|&b| b != magic) {
|
||||
if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) {
|
||||
// Read progress validity
|
||||
if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE {
|
||||
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
|
||||
} else {
|
||||
self.aligned.rotate_left(STATE::WRITE_SIZE);
|
||||
}
|
||||
|
||||
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
||||
if self.aligned[..STATE::WRITE_SIZE]
|
||||
.iter()
|
||||
.any(|&b| b != STATE_ERASE_VALUE)
|
||||
{
|
||||
// The current progress validity marker is invalid
|
||||
} else {
|
||||
// Invalidate progress
|
||||
self.aligned.fill(!STATE_ERASE_VALUE);
|
||||
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
|
||||
self.state
|
||||
.write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE])
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Clear magic and progress
|
||||
@ -293,7 +351,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
|
||||
// Set magic
|
||||
self.aligned.fill(magic);
|
||||
self.state.write(0, &self.aligned).await?;
|
||||
self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -329,4 +387,76 @@ mod tests {
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_bigger_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(1024) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_smaller_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(2048) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_cross_sector_boundary() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(896) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
|
||||
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
dfu: DFU,
|
||||
state: BlockingFirmwareState<'d, STATE>,
|
||||
last_erased_dfu_sector_index: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
@ -91,6 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
Self {
|
||||
dfu: config.dfu,
|
||||
state: BlockingFirmwareState::new(config.state, aligned),
|
||||
last_erased_dfu_sector_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +109,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
/// proceed with updating the firmware as it must be signed with a
|
||||
/// corresponding private key (otherwise it could be malicious firmware).
|
||||
///
|
||||
/// Mark to trigger firmware swap on next boot if verify suceeds.
|
||||
/// Mark to trigger firmware swap on next boot if verify succeeds.
|
||||
///
|
||||
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
|
||||
/// been generated from a SHA-512 digest of the firmware bytes.
|
||||
@ -207,20 +209,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
self.state.mark_booted()
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
/// Writes firmware data to the device.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
/// This function writes the given data to the firmware area starting at the specified offset.
|
||||
/// It handles sector erasures and data writes while verifying the device is in a proper state
|
||||
/// for firmware updates. The function ensures that only unerased sectors are erased before
|
||||
/// writing and efficiently handles the writing process across sector boundaries and in
|
||||
/// various configurations (data size, sector size, etc.).
|
||||
///
|
||||
/// # Safety
|
||||
/// # Arguments
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
/// * `offset` - The starting offset within the firmware area where data writing should begin.
|
||||
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
|
||||
/// multiple of NorFlash WRITE_SIZE.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
///
|
||||
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
|
||||
/// - There is a failure erasing a sector before writing.
|
||||
/// - There is a failure writing data to the device.
|
||||
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||
self.state.verify_booted()?;
|
||||
|
||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
||||
// Initialize variables to keep track of the remaining data and the current offset.
|
||||
let mut remaining_data = data;
|
||||
let mut offset = offset;
|
||||
|
||||
self.dfu.write(offset as u32, data)?;
|
||||
// Continue writing as long as there is data left to write.
|
||||
while !remaining_data.is_empty() {
|
||||
// Compute the current sector and its boundaries.
|
||||
let current_sector = offset / DFU::ERASE_SIZE;
|
||||
let sector_start = current_sector * DFU::ERASE_SIZE;
|
||||
let sector_end = sector_start + DFU::ERASE_SIZE;
|
||||
// Determine if the current sector needs to be erased before writing.
|
||||
let need_erase = self
|
||||
.last_erased_dfu_sector_index
|
||||
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
|
||||
|
||||
// If the sector needs to be erased, erase it and update the last erased sector index.
|
||||
if need_erase {
|
||||
self.dfu.erase(sector_start as u32, sector_end as u32)?;
|
||||
self.last_erased_dfu_sector_index = Some(current_sector);
|
||||
}
|
||||
|
||||
// Calculate the size of the data chunk that can be written in the current iteration.
|
||||
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
|
||||
// Split the data to get the current chunk to be written and the remaining data.
|
||||
let (data_chunk, rest) = remaining_data.split_at(write_size);
|
||||
|
||||
// Write the current data chunk.
|
||||
self.dfu.write(offset as u32, data_chunk)?;
|
||||
|
||||
// Update the offset and remaining data for the next iteration.
|
||||
remaining_data = rest;
|
||||
offset += write_size;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -368,4 +418,82 @@ mod tests {
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_bigger_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(1024) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_smaller_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(2048) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_cross_sector_boundary() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(896) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,11 @@ categories = [
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
|
||||
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
|
||||
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
|
||||
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
||||
//! Pulse Density Modulation (PDM) mirophone driver
|
||||
|
||||
#![macro_use]
|
||||
|
||||
@ -26,7 +26,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
|
||||
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
/// Interrupt handler
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
@ -56,12 +56,12 @@ pub struct Pdm<'d, T: Instance> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// PDM error.
|
||||
/// PDM error
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Buffer is too long.
|
||||
/// Buffer is too long
|
||||
BufferTooLong,
|
||||
/// Buffer is empty
|
||||
BufferZeroLength,
|
||||
@ -75,13 +75,13 @@ static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||
|
||||
/// The state of a continuously running sampler. While it reflects
|
||||
/// the progress of a sampler, it also signals what should be done
|
||||
/// next. For example, if the sampler has stopped then the Pdm implementation
|
||||
/// can then tear down its infrastructure.
|
||||
/// next. For example, if the sampler has stopped then the PDM implementation
|
||||
/// can then tear down its infrastructure
|
||||
#[derive(PartialEq)]
|
||||
pub enum SamplerState {
|
||||
/// The sampler processed the samples and is ready for more.
|
||||
/// The sampler processed the samples and is ready for more
|
||||
Sampled,
|
||||
/// The sampler is done processing samples.
|
||||
/// The sampler is done processing samples
|
||||
Stopped,
|
||||
}
|
||||
|
||||
@ -145,15 +145,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
}
|
||||
|
||||
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
||||
let gain_left = gain_left
|
||||
.saturating_add(I7F1::from_bits(40))
|
||||
.saturating_to_num::<u8>()
|
||||
.clamp(0, 0x50);
|
||||
let gain_right = gain_right
|
||||
.saturating_add(I7F1::from_bits(40))
|
||||
.saturating_to_num::<u8>()
|
||||
.clamp(0, 0x50);
|
||||
|
||||
let gain_to_bits = |gain: I7F1| -> u8 {
|
||||
let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);
|
||||
unsafe { core::mem::transmute(gain) }
|
||||
};
|
||||
let gain_left = gain_to_bits(gain_left);
|
||||
let gain_right = gain_to_bits(gain_right);
|
||||
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
||||
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
||||
}
|
||||
@ -163,12 +160,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
Self::_set_gain(T::regs(), gain_left, gain_right)
|
||||
}
|
||||
|
||||
/// Start sampling microphon data into a dummy buffer
|
||||
/// Usefull to start the microphon and keep it active between recording samples
|
||||
/// Start sampling microphone data into a dummy buffer.
|
||||
/// Useful to start the microphone and keep it active between recording samples.
|
||||
pub async fn start(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
// start dummy sampling because microphon needs some setup time
|
||||
// start dummy sampling because microphone needs some setup time
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
@ -179,14 +176,14 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// Stop sampling microphon data inta a dummy buffer
|
||||
/// Stop sampling microphone data inta a dummy buffer
|
||||
pub async fn stop(&mut self) {
|
||||
let r = T::regs();
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
r.events_started.reset();
|
||||
}
|
||||
|
||||
/// Sample data into the given buffer.
|
||||
/// Sample data into the given buffer
|
||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
@ -303,7 +300,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
});
|
||||
|
||||
// Don't reorder the start event before the previous writes. Hopefully self
|
||||
// wouldn't happen anyway.
|
||||
// wouldn't happen anyway
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
@ -314,11 +311,11 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
|
||||
let drop = OnDrop::new(|| {
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
// N.B. It would be better if this were async, but Drop only support sync code.
|
||||
// N.B. It would be better if this were async, but Drop only support sync code
|
||||
while r.events_stopped.read().bits() != 0 {}
|
||||
});
|
||||
|
||||
// Wait for events and complete when the sampler indicates it has had enough.
|
||||
// Wait for events and complete when the sampler indicates it has had enough
|
||||
poll_fn(|cx| {
|
||||
let r = T::regs();
|
||||
|
||||
@ -331,7 +328,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
if !done {
|
||||
// Discard the last buffer after the user requested a stop.
|
||||
// Discard the last buffer after the user requested a stop
|
||||
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
||||
let next_buffer = 1 - current_buffer;
|
||||
current_buffer = next_buffer;
|
||||
@ -405,7 +402,7 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM operation mode.
|
||||
/// PDM operation mode
|
||||
#[derive(PartialEq)]
|
||||
pub enum OperationMode {
|
||||
/// Mono (1 channel)
|
||||
@ -476,9 +473,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM peripheral instance.
|
||||
/// PDM peripheral instance
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
/// Interrupt for this peripheral
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
||||
/// SPIM error
|
||||
@ -25,10 +25,6 @@ use crate::{interrupt, pac, Peripheral};
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Supplied TX buffer overflows EasyDMA transmit buffer
|
||||
TxBufferTooLong,
|
||||
/// Supplied RX buffer overflows EasyDMA receive buffer
|
||||
RxBufferTooLong,
|
||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||
BufferNotInRAM,
|
||||
}
|
||||
@ -74,10 +70,14 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
let s = T::state();
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
{
|
||||
// Ideally we should call this only during the first chunk transfer,
|
||||
// but so far calling this every time doesn't seem to be causing any issues.
|
||||
if r.events_started.read().bits() != 0 {
|
||||
s.waker.wake();
|
||||
r.intenclr.write(|w| w.started().clear());
|
||||
}
|
||||
}
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
s.waker.wake();
|
||||
@ -209,35 +209,39 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
spim
|
||||
}
|
||||
|
||||
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
|
||||
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||
// slice can only be built from data located in RAM.
|
||||
|
||||
fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Set up the DMA write.
|
||||
let (ptr, tx_len) = slice_ptr_parts(tx);
|
||||
if tx_len > EASY_DMA_SIZE {
|
||||
return Err(Error::TxBufferTooLong);
|
||||
fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) {
|
||||
if total > offset {
|
||||
(ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length))
|
||||
} else {
|
||||
(ptr, 0)
|
||||
}
|
||||
}
|
||||
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
||||
|
||||
// Set up the DMA read.
|
||||
let (ptr, rx_len) = slice_ptr_parts_mut(rx);
|
||||
if rx_len > EASY_DMA_SIZE {
|
||||
return Err(Error::RxBufferTooLong);
|
||||
}
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
let (ptr, len) = slice_ptr_parts_mut(rx);
|
||||
let (rx_ptr, rx_len) = xfer_params(ptr as _, len as _, offset, length);
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx_ptr) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
||||
|
||||
// Set up the DMA write.
|
||||
let (ptr, len) = slice_ptr_parts(tx);
|
||||
let (tx_ptr, tx_len) = xfer_params(ptr as _, len as _, offset, length);
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx_ptr) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
||||
|
||||
/*
|
||||
trace!("XFER: offset: {}, length: {}", offset, length);
|
||||
trace!("RX(len: {}, ptr: {=u32:02x})", rx_len, rx_ptr as u32);
|
||||
trace!("TX(len: {}, ptr: {=u32:02x})", tx_len, tx_ptr as u32);
|
||||
*/
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
{
|
||||
if offset == 0 {
|
||||
let s = T::state();
|
||||
|
||||
r.events_started.reset();
|
||||
@ -260,21 +264,32 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
|
||||
// Start SPI transaction.
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
self.prepare(rx, tx)?;
|
||||
fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
|
||||
self.prepare_dma_transfer(rx, tx, offset, length);
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
while let Poll::Pending = self.nrf52832_dma_workaround_status() {}
|
||||
if offset == 0 {
|
||||
while self.nrf52832_dma_workaround_status().is_pending() {}
|
||||
}
|
||||
|
||||
// Wait for 'end' event.
|
||||
while T::regs().events_end.read().bits() == 0 {}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
|
||||
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||
// slice can only be built from data located in RAM.
|
||||
|
||||
let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
|
||||
for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
|
||||
let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
|
||||
self.blocking_inner_from_ram_chunk(rx, tx, offset, length);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -287,14 +302,14 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
self.blocking_inner_from_ram(rx, tx_ram_buf)
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
self.prepare(rx, tx)?;
|
||||
async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
|
||||
self.prepare_dma_transfer(rx, tx, offset, length);
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
if offset == 0 {
|
||||
poll_fn(|cx| {
|
||||
let s = T::state();
|
||||
|
||||
@ -303,6 +318,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
self.nrf52832_dma_workaround_status()
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// Wait for 'end' event.
|
||||
poll_fn(|cx| {
|
||||
@ -316,7 +332,18 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
|
||||
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||
// slice can only be built from data located in RAM.
|
||||
|
||||
let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
|
||||
for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
|
||||
let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
|
||||
self.async_inner_from_ram_chunk(rx, tx, offset, length).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -329,7 +356,6 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
self.async_inner_from_ram(rx, tx_ram_buf).await
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,8 +554,6 @@ mod eh02 {
|
||||
impl embedded_hal_1::spi::Error for Error {
|
||||
fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
|
||||
match *self {
|
||||
Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
|
||||
Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
|
||||
Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,19 @@ use core::mem;
|
||||
const SRAM_LOWER: usize = 0x2000_0000;
|
||||
const SRAM_UPPER: usize = 0x3000_0000;
|
||||
|
||||
// #![feature(const_slice_ptr_len)]
|
||||
// https://github.com/rust-lang/rust/issues/71146
|
||||
pub(crate) fn slice_ptr_len<T>(ptr: *const [T]) -> usize {
|
||||
use core::ptr::NonNull;
|
||||
let ptr = ptr.cast_mut();
|
||||
if let Some(ptr) = NonNull::new(ptr) {
|
||||
ptr.len()
|
||||
} else {
|
||||
// We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null.
|
||||
NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace transmutes with core::ptr::metadata once it's stable
|
||||
pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) {
|
||||
unsafe { mem::transmute(slice) }
|
||||
|
@ -889,6 +889,17 @@ pub struct AnyPin {
|
||||
pin_bank: u8,
|
||||
}
|
||||
|
||||
impl AnyPin {
|
||||
/// Unsafely create a new type-erased pin.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you’re only using one instance of this type at a time.
|
||||
pub unsafe fn steal(pin_bank: u8) -> Self {
|
||||
Self { pin_bank }
|
||||
}
|
||||
}
|
||||
|
||||
impl_peripheral!(AnyPin);
|
||||
|
||||
impl Pin for AnyPin {}
|
||||
|
@ -21,6 +21,16 @@ pub enum Error {
|
||||
Abort(AbortReason),
|
||||
/// User passed in a response buffer that was 0 length
|
||||
InvalidResponseBufferLength,
|
||||
/// The response buffer length was too short to contain the message
|
||||
///
|
||||
/// The length parameter will always be the length of the buffer, and is
|
||||
/// provided as a convenience for matching alongside `Command::Write`.
|
||||
PartialWrite(usize),
|
||||
/// The response buffer length was too short to contain the message
|
||||
///
|
||||
/// The length parameter will always be the length of the buffer, and is
|
||||
/// provided as a convenience for matching alongside `Command::GeneralCall`.
|
||||
PartialGeneralCall(usize),
|
||||
}
|
||||
|
||||
/// Received command
|
||||
@ -56,17 +66,23 @@ pub enum ReadStatus {
|
||||
pub struct Config {
|
||||
/// Target Address
|
||||
pub addr: u16,
|
||||
/// Control if the peripheral should ack to and report general calls.
|
||||
pub general_call: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self { addr: 0x55 }
|
||||
Self {
|
||||
addr: 0x55,
|
||||
general_call: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// I2CSlave driver.
|
||||
pub struct I2cSlave<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
pending_byte: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
@ -96,7 +112,19 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
w.set_master_mode(false);
|
||||
w.set_ic_slave_disable(false);
|
||||
w.set_tx_empty_ctrl(true);
|
||||
w.set_rx_fifo_full_hld_ctrl(true);
|
||||
|
||||
// This typically makes no sense for a slave, but it is used to
|
||||
// tune spike suppression, according to the datasheet.
|
||||
w.set_speed(pac::i2c::vals::Speed::FAST);
|
||||
|
||||
// Generate stop interrupts for general calls
|
||||
// This also causes stop interrupts for other devices on the bus but those will not be
|
||||
// propagated up to the application.
|
||||
w.set_stop_det_ifaddressed(!config.general_call);
|
||||
});
|
||||
p.ic_ack_general_call()
|
||||
.write(|w| w.set_ack_gen_call(config.general_call));
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0. Rx watermark should never change, but Tx watermark will be
|
||||
@ -119,7 +147,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
pending_byte: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `f` to check if we are ready or not.
|
||||
@ -133,8 +164,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
future::poll_fn(|cx| {
|
||||
let r = f(self);
|
||||
|
||||
trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
|
||||
|
||||
if r.is_pending() {
|
||||
T::waker().register(cx.waker());
|
||||
g(self);
|
||||
@ -146,14 +175,36 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize {
|
||||
fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
|
||||
let p = T::regs();
|
||||
let len = p.ic_rxflr().read().rxflr() as usize;
|
||||
let end = offset + len;
|
||||
for i in offset..end {
|
||||
buffer[i] = p.ic_data_cmd().read().dat();
|
||||
|
||||
for b in &mut buffer[*offset..] {
|
||||
if let Some(pending) = self.pending_byte.take() {
|
||||
*b = pending;
|
||||
*offset += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let status = p.ic_status().read();
|
||||
if !status.rfne() {
|
||||
break;
|
||||
}
|
||||
|
||||
let dat = p.ic_data_cmd().read();
|
||||
if *offset != 0 && dat.first_data_byte() {
|
||||
// The RP2040 state machine will keep placing bytes into the
|
||||
// FIFO, even if they are part of a subsequent write transaction.
|
||||
//
|
||||
// Unfortunately merely reading ic_data_cmd will consume that
|
||||
// byte, the first byte of the next transaction, so we need
|
||||
// to store it elsewhere
|
||||
self.pending_byte = Some(dat.dat());
|
||||
break;
|
||||
}
|
||||
|
||||
*b = dat.dat();
|
||||
*offset += 1;
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -165,32 +216,46 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
}
|
||||
|
||||
/// Wait asynchronously for commands from an I2C master.
|
||||
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
|
||||
/// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.
|
||||
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
|
||||
let p = T::regs();
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
// set rx fifo watermark to 1 byte
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
let mut len = 0;
|
||||
let ret = self
|
||||
.wait_on(
|
||||
self.wait_on(
|
||||
|me| {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
|
||||
if p.ic_rxflr().read().rxflr() > 0 {
|
||||
len = me.drain_fifo(buffer, len);
|
||||
// we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise
|
||||
me.drain_fifo(buffer, &mut len);
|
||||
// we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(11));
|
||||
}
|
||||
|
||||
if buffer.len() == len {
|
||||
if stat.gen_call() {
|
||||
return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));
|
||||
} else {
|
||||
return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
|
||||
}
|
||||
}
|
||||
|
||||
if stat.restart_det() && stat.rd_req() {
|
||||
p.ic_clr_restart_det().read();
|
||||
Poll::Ready(Ok(Command::WriteRead(len)))
|
||||
} else if stat.gen_call() && stat.stop_det() && len > 0 {
|
||||
p.ic_clr_gen_call().read();
|
||||
p.ic_clr_stop_det().read();
|
||||
Poll::Ready(Ok(Command::GeneralCall(len)))
|
||||
} else if stat.stop_det() {
|
||||
} else if stat.stop_det() && len > 0 {
|
||||
p.ic_clr_stop_det().read();
|
||||
Poll::Ready(Ok(Command::Write(len)))
|
||||
} else if stat.rd_req() {
|
||||
p.ic_clr_stop_det().read();
|
||||
p.ic_clr_restart_det().read();
|
||||
p.ic_clr_gen_call().read();
|
||||
Poll::Ready(Ok(Command::Read))
|
||||
} else {
|
||||
Poll::Pending
|
||||
@ -206,11 +271,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
});
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
ret
|
||||
.await
|
||||
}
|
||||
|
||||
/// Respond to an I2C master READ command, asynchronously.
|
||||
@ -223,11 +284,11 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
|
||||
let mut chunks = buffer.chunks(FIFO_SIZE as usize);
|
||||
|
||||
let ret = self
|
||||
.wait_on(
|
||||
self.wait_on(
|
||||
|me| {
|
||||
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
|
||||
if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
|
||||
p.ic_clr_intr().read();
|
||||
return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
|
||||
} else {
|
||||
return Poll::Ready(Err(abort_reason));
|
||||
@ -237,13 +298,17 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
if let Some(chunk) = chunks.next() {
|
||||
me.write_to_fifo(chunk);
|
||||
|
||||
p.ic_clr_rd_req().read();
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
|
||||
if stat.rx_done() && stat.stop_det() {
|
||||
p.ic_clr_rx_done().read();
|
||||
p.ic_clr_stop_det().read();
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() {
|
||||
} else if stat.rd_req() && stat.tx_empty() {
|
||||
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
|
||||
} else {
|
||||
Poll::Pending
|
||||
@ -259,11 +324,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
})
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
ret
|
||||
.await
|
||||
}
|
||||
|
||||
/// Respond to reads with the fill byte until the controller stops asking
|
||||
@ -294,10 +355,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
let p = T::regs();
|
||||
let mut abort_reason = p.ic_tx_abrt_source().read();
|
||||
|
||||
// Mask off fifo flush count
|
||||
let tx_flush_cnt = abort_reason.tx_flush_cnt();
|
||||
abort_reason.set_tx_flush_cnt(0);
|
||||
|
||||
// Mask off master_dis
|
||||
abort_reason.set_abrt_master_dis(false);
|
||||
|
||||
@ -314,8 +371,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
AbortReason::NoAcknowledge
|
||||
} else if abort_reason.arb_lost() {
|
||||
AbortReason::ArbitrationLoss
|
||||
} else if abort_reason.abrt_slvflush_txfifo() {
|
||||
AbortReason::TxNotEmpty(tx_flush_cnt)
|
||||
} else if abort_reason.tx_flush_cnt() > 0 {
|
||||
AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt())
|
||||
} else {
|
||||
AbortReason::Other(abort_reason.0)
|
||||
};
|
||||
|
@ -68,7 +68,7 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
critical-section = "1.1"
|
||||
#stm32-metapac = { version = "15" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -5,8 +5,7 @@ use std::{env, fs};
|
||||
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet};
|
||||
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
|
||||
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccKernelClock, StopMode, METADATA};
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
@ -183,40 +182,33 @@ fn main() {
|
||||
|
||||
let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
|
||||
None => "",
|
||||
Some("tim1") => "TIM1",
|
||||
Some("tim2") => "TIM2",
|
||||
Some("tim3") => "TIM3",
|
||||
Some("tim4") => "TIM4",
|
||||
Some("tim5") => "TIM5",
|
||||
Some("tim8") => "TIM8",
|
||||
Some("tim9") => "TIM9",
|
||||
Some("tim11") => "TIM11",
|
||||
Some("tim12") => "TIM12",
|
||||
Some("tim15") => "TIM15",
|
||||
Some("tim20") => "TIM20",
|
||||
Some("tim21") => "TIM21",
|
||||
Some("tim22") => "TIM22",
|
||||
Some("tim23") => "TIM23",
|
||||
Some("tim24") => "TIM24",
|
||||
Some("any") => {
|
||||
if singletons.contains(&"TIM2".to_string()) {
|
||||
"TIM2"
|
||||
} else if singletons.contains(&"TIM3".to_string()) {
|
||||
"TIM3"
|
||||
} else if singletons.contains(&"TIM4".to_string()) {
|
||||
"TIM4"
|
||||
} else if singletons.contains(&"TIM5".to_string()) {
|
||||
"TIM5"
|
||||
} else if singletons.contains(&"TIM9".to_string()) {
|
||||
"TIM9"
|
||||
} else if singletons.contains(&"TIM11".to_string()) {
|
||||
"TIM11"
|
||||
} else if singletons.contains(&"TIM12".to_string()) {
|
||||
"TIM12"
|
||||
} else if singletons.contains(&"TIM15".to_string()) {
|
||||
"TIM15"
|
||||
} else if singletons.contains(&"TIM21".to_string()) {
|
||||
"TIM21"
|
||||
} else if singletons.contains(&"TIM22".to_string()) {
|
||||
"TIM22"
|
||||
} else {
|
||||
panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM9, TIM11, TIM12 or TIM15.")
|
||||
}
|
||||
// Order of TIM candidators:
|
||||
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
|
||||
// 2. In same catagory: larger TIM number first
|
||||
[
|
||||
"TIM22", "TIM21", "TIM12", "TIM9", // 2CH
|
||||
"TIM15", // 2CH_CMP
|
||||
"TIM19", "TIM4", "TIM3", // GP16
|
||||
"TIM24", "TIM23", "TIM5", "TIM2", // GP32
|
||||
"TIM20", "TIM8", "TIM1", //ADV
|
||||
]
|
||||
.iter()
|
||||
.find(|tim| singletons.contains(&tim.to_string())).expect("time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.")
|
||||
}
|
||||
_ => panic!("unknown time_driver {:?}", time_driver),
|
||||
};
|
||||
@ -414,38 +406,6 @@ fn main() {
|
||||
.find(|r| r.kind == "rcc")
|
||||
.unwrap();
|
||||
|
||||
// ========
|
||||
// Generate rcc fieldset and enum maps
|
||||
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
||||
let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
|
||||
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
|
||||
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
|
||||
|
||||
rcc_blocks
|
||||
.iter()
|
||||
.filter_map(|b| match &b.inner {
|
||||
BlockItemInner::Register(register) => register.fieldset.map(|f| (b.name, f)),
|
||||
_ => None,
|
||||
})
|
||||
.filter_map(|(b, f)| {
|
||||
rcc_fieldsets.get(f).map(|f| {
|
||||
(
|
||||
b,
|
||||
f.fields
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
let enumm = f.enumm?;
|
||||
let enumm = rcc_enums.get(enumm)?;
|
||||
|
||||
Some((f.name, *enumm))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// ========
|
||||
// Generate RccPeripheral impls
|
||||
|
||||
@ -494,8 +454,8 @@ fn main() {
|
||||
|
||||
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
|
||||
let pname = format_ident!("{}", p.name);
|
||||
let en_reg = format_ident!("{}", en.register);
|
||||
let set_en_field = format_ident!("set_{}", en.field);
|
||||
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
|
||||
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
|
||||
|
||||
let refcount =
|
||||
force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1;
|
||||
@ -523,21 +483,25 @@ fn main() {
|
||||
(TokenStream::new(), TokenStream::new())
|
||||
};
|
||||
|
||||
let mux_for = |mux: Option<&'static PeripheralRccRegister>| {
|
||||
let mux = mux?;
|
||||
let fieldset = rcc_enum_map.get(mux.register)?;
|
||||
let enumm = fieldset.get(mux.field)?;
|
||||
let clock_frequency = match &rcc.kernel_clock {
|
||||
PeripheralRccKernelClock::Mux(mux) => {
|
||||
let ir = &rcc_registers.ir;
|
||||
let fieldset_name = mux.register.to_ascii_lowercase();
|
||||
let fieldset = ir
|
||||
.fieldsets
|
||||
.iter()
|
||||
.find(|i| i.name.eq_ignore_ascii_case(&fieldset_name))
|
||||
.unwrap();
|
||||
let field_name = mux.field.to_ascii_lowercase();
|
||||
let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap();
|
||||
let enum_name = field.enumm.unwrap();
|
||||
let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap();
|
||||
|
||||
Some((mux, *enumm))
|
||||
};
|
||||
let fieldset_name = format_ident!("{}", fieldset_name);
|
||||
let field_name = format_ident!("{}", field_name);
|
||||
let enum_name = format_ident!("{}", enum_name);
|
||||
|
||||
let clock_frequency = match mux_for(rcc.mux.as_ref()) {
|
||||
Some((mux, rcc_enumm)) => {
|
||||
let fieldset_name = format_ident!("{}", mux.register);
|
||||
let field_name = format_ident!("{}", mux.field);
|
||||
let enum_name = format_ident!("{}", rcc_enumm.name);
|
||||
|
||||
let match_arms: TokenStream = rcc_enumm
|
||||
let match_arms: TokenStream = enumm
|
||||
.variants
|
||||
.iter()
|
||||
.filter(|v| v.name != "DISABLE")
|
||||
@ -561,9 +525,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let clock_name = format_ident!("{}", rcc.clock);
|
||||
clock_names.insert(rcc.clock.to_string());
|
||||
PeripheralRccKernelClock::Clock(clock) => {
|
||||
let clock = clock.to_ascii_lowercase();
|
||||
let clock_name = format_ident!("{}", clock);
|
||||
clock_names.insert(clock.to_string());
|
||||
quote! {
|
||||
unsafe { crate::rcc::get_freqs().#clock_name.unwrap() }
|
||||
}
|
||||
@ -1056,6 +1021,7 @@ fn main() {
|
||||
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
|
||||
(("timer", "UP"), quote!(crate::timer::UpDma)),
|
||||
(("hash", "IN"), quote!(crate::hash::Dma)),
|
||||
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
|
||||
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
|
||||
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
|
||||
|
@ -249,7 +249,7 @@ impl From<Pull> for vals::Pupdr {
|
||||
|
||||
/// Speed settings
|
||||
///
|
||||
/// These vary dpeending on the chip, ceck the reference manual or datasheet for details.
|
||||
/// These vary depending on the chip, check the reference manual or datasheet for details.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
592
embassy-stm32/src/hash/mod.rs
Normal file
592
embassy-stm32/src/hash/mod.rs
Normal file
@ -0,0 +1,592 @@
|
||||
//! Hash generator (HASH)
|
||||
use core::cmp::min;
|
||||
#[cfg(hash_v2)]
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(hash_v2)]
|
||||
use core::ptr;
|
||||
#[cfg(hash_v2)]
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use stm32_metapac::hash::regs::*;
|
||||
|
||||
use crate::dma::NoDma;
|
||||
#[cfg(hash_v2)]
|
||||
use crate::dma::Transfer;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::peripherals::HASH;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
|
||||
#[cfg(hash_v1)]
|
||||
const NUM_CONTEXT_REGS: usize = 51;
|
||||
#[cfg(hash_v3)]
|
||||
const NUM_CONTEXT_REGS: usize = 103;
|
||||
#[cfg(any(hash_v2, hash_v4))]
|
||||
const NUM_CONTEXT_REGS: usize = 54;
|
||||
|
||||
const HASH_BUFFER_LEN: usize = 132;
|
||||
const DIGEST_BLOCK_SIZE: usize = 128;
|
||||
|
||||
static HASH_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
/// HASH interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let bits = T::regs().sr().read();
|
||||
if bits.dinis() {
|
||||
T::regs().imr().modify(|reg| reg.set_dinie(false));
|
||||
HASH_WAKER.wake();
|
||||
}
|
||||
if bits.dcis() {
|
||||
T::regs().imr().modify(|reg| reg.set_dcie(false));
|
||||
HASH_WAKER.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Hash algorithm selection
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Algorithm {
|
||||
/// SHA-1 Algorithm
|
||||
SHA1 = 0,
|
||||
|
||||
#[cfg(any(hash_v1, hash_v2, hash_v4))]
|
||||
/// MD5 Algorithm
|
||||
MD5 = 1,
|
||||
|
||||
/// SHA-224 Algorithm
|
||||
SHA224 = 2,
|
||||
|
||||
/// SHA-256 Algorithm
|
||||
SHA256 = 3,
|
||||
|
||||
#[cfg(hash_v3)]
|
||||
/// SHA-384 Algorithm
|
||||
SHA384 = 12,
|
||||
|
||||
#[cfg(hash_v3)]
|
||||
/// SHA-512/224 Algorithm
|
||||
SHA512_224 = 13,
|
||||
|
||||
#[cfg(hash_v3)]
|
||||
/// SHA-512/256 Algorithm
|
||||
SHA512_256 = 14,
|
||||
|
||||
#[cfg(hash_v3)]
|
||||
/// SHA-256 Algorithm
|
||||
SHA512 = 15,
|
||||
}
|
||||
|
||||
/// Input data width selection
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DataType {
|
||||
///32-bit data, no data is swapped.
|
||||
Width32 = 0,
|
||||
///16-bit data, each half-word is swapped.
|
||||
Width16 = 1,
|
||||
///8-bit data, all bytes are swapped.
|
||||
Width8 = 2,
|
||||
///1-bit data, all bits are swapped.
|
||||
Width1 = 3,
|
||||
}
|
||||
|
||||
/// Stores the state of the HASH peripheral for suspending/resuming
|
||||
/// digest calculation.
|
||||
pub struct Context<'c> {
|
||||
first_word_sent: bool,
|
||||
key_sent: bool,
|
||||
buffer: [u8; HASH_BUFFER_LEN],
|
||||
buflen: usize,
|
||||
algo: Algorithm,
|
||||
format: DataType,
|
||||
imr: u32,
|
||||
str: u32,
|
||||
cr: u32,
|
||||
csr: [u32; NUM_CONTEXT_REGS],
|
||||
key: HmacKey<'c>,
|
||||
}
|
||||
|
||||
type HmacKey<'k> = Option<&'k [u8]>;
|
||||
|
||||
/// HASH driver.
|
||||
pub struct Hash<'d, T: Instance, D = NoDma> {
|
||||
_peripheral: PeripheralRef<'d, T>,
|
||||
#[allow(dead_code)]
|
||||
dma: PeripheralRef<'d, D>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, D> Hash<'d, T, D> {
|
||||
/// Instantiates, resets, and enables the HASH peripheral.
|
||||
pub fn new(
|
||||
peripheral: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = D> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
HASH::enable_and_reset();
|
||||
into_ref!(peripheral, dma);
|
||||
let instance = Self {
|
||||
_peripheral: peripheral,
|
||||
dma: dma,
|
||||
};
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
instance
|
||||
}
|
||||
|
||||
/// Starts computation of a new hash and returns the saved peripheral state.
|
||||
pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> {
|
||||
// Define a context for this new computation.
|
||||
let mut ctx = Context {
|
||||
first_word_sent: false,
|
||||
key_sent: false,
|
||||
buffer: [0; HASH_BUFFER_LEN],
|
||||
buflen: 0,
|
||||
algo: algorithm,
|
||||
format: format,
|
||||
imr: 0,
|
||||
str: 0,
|
||||
cr: 0,
|
||||
csr: [0; NUM_CONTEXT_REGS],
|
||||
key,
|
||||
};
|
||||
|
||||
// Set the data type in the peripheral.
|
||||
T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
|
||||
|
||||
// Select the algorithm.
|
||||
#[cfg(hash_v1)]
|
||||
if ctx.algo == Algorithm::MD5 {
|
||||
T::regs().cr().modify(|w| w.set_algo(true));
|
||||
}
|
||||
|
||||
#[cfg(hash_v2)]
|
||||
{
|
||||
// Select the algorithm.
|
||||
let mut algo0 = false;
|
||||
let mut algo1 = false;
|
||||
if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
|
||||
algo0 = true;
|
||||
}
|
||||
if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
|
||||
algo1 = true;
|
||||
}
|
||||
T::regs().cr().modify(|w| w.set_algo0(algo0));
|
||||
T::regs().cr().modify(|w| w.set_algo1(algo1));
|
||||
}
|
||||
|
||||
#[cfg(any(hash_v3, hash_v4))]
|
||||
T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));
|
||||
|
||||
// Configure HMAC mode if a key is provided.
|
||||
if let Some(key) = ctx.key {
|
||||
T::regs().cr().modify(|w| w.set_mode(true));
|
||||
if key.len() > 64 {
|
||||
T::regs().cr().modify(|w| w.set_lkey(true));
|
||||
}
|
||||
}
|
||||
|
||||
T::regs().cr().modify(|w| w.set_init(true));
|
||||
|
||||
// Store and return the state of the peripheral.
|
||||
self.store_context(&mut ctx);
|
||||
ctx
|
||||
}
|
||||
|
||||
/// Restores the peripheral state using the given context,
|
||||
/// then updates the state with the provided data.
|
||||
/// Peripheral state is saved upon return.
|
||||
pub fn update_blocking<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) {
|
||||
// Restore the peripheral state.
|
||||
self.load_context(&ctx);
|
||||
|
||||
// Load the HMAC key if provided.
|
||||
if !ctx.key_sent {
|
||||
if let Some(key) = ctx.key {
|
||||
self.accumulate_blocking(key);
|
||||
T::regs().str().write(|w| w.set_dcal(true));
|
||||
// Block waiting for digest.
|
||||
while !T::regs().sr().read().dinis() {}
|
||||
}
|
||||
ctx.key_sent = true;
|
||||
}
|
||||
|
||||
let mut data_waiting = input.len() + ctx.buflen;
|
||||
if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
|
||||
// There isn't enough data to digest a block, so append it to the buffer.
|
||||
ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
|
||||
ctx.buflen += input.len();
|
||||
self.store_context(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut ilen_remaining = input.len();
|
||||
let mut input_start = 0;
|
||||
|
||||
// Handle first block.
|
||||
if !ctx.first_word_sent {
|
||||
let empty_len = ctx.buffer.len() - ctx.buflen;
|
||||
let copy_len = min(empty_len, ilen_remaining);
|
||||
// Fill the buffer.
|
||||
if copy_len > 0 {
|
||||
ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);
|
||||
ctx.buflen += copy_len;
|
||||
ilen_remaining -= copy_len;
|
||||
input_start += copy_len;
|
||||
}
|
||||
self.accumulate_blocking(ctx.buffer.as_slice());
|
||||
data_waiting -= ctx.buflen;
|
||||
ctx.buflen = 0;
|
||||
ctx.first_word_sent = true;
|
||||
}
|
||||
|
||||
if data_waiting < DIGEST_BLOCK_SIZE {
|
||||
// There isn't enough data remaining to process another block, so store it.
|
||||
ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
|
||||
ctx.buflen += ilen_remaining;
|
||||
} else {
|
||||
// First ingest the data in the buffer.
|
||||
let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
|
||||
if empty_len > 0 {
|
||||
let copy_len = min(empty_len, ilen_remaining);
|
||||
ctx.buffer[ctx.buflen..ctx.buflen + copy_len]
|
||||
.copy_from_slice(&input[input_start..input_start + copy_len]);
|
||||
ctx.buflen += copy_len;
|
||||
ilen_remaining -= copy_len;
|
||||
input_start += copy_len;
|
||||
}
|
||||
self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]);
|
||||
ctx.buflen = 0;
|
||||
|
||||
// Move any extra data to the now-empty buffer.
|
||||
let leftovers = ilen_remaining % 64;
|
||||
if leftovers > 0 {
|
||||
ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
|
||||
ctx.buflen += leftovers;
|
||||
ilen_remaining -= leftovers;
|
||||
}
|
||||
|
||||
// Hash the remaining data.
|
||||
self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]);
|
||||
}
|
||||
|
||||
// Save the peripheral context.
|
||||
self.store_context(ctx);
|
||||
}
|
||||
|
||||
/// Restores the peripheral state using the given context,
|
||||
/// then updates the state with the provided data.
|
||||
/// Peripheral state is saved upon return.
|
||||
#[cfg(hash_v2)]
|
||||
pub async fn update<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8])
|
||||
where
|
||||
D: crate::hash::Dma<T>,
|
||||
{
|
||||
// Restore the peripheral state.
|
||||
self.load_context(&ctx);
|
||||
|
||||
// Load the HMAC key if provided.
|
||||
if !ctx.key_sent {
|
||||
if let Some(key) = ctx.key {
|
||||
self.accumulate(key).await;
|
||||
}
|
||||
ctx.key_sent = true;
|
||||
}
|
||||
|
||||
let data_waiting = input.len() + ctx.buflen;
|
||||
if data_waiting < DIGEST_BLOCK_SIZE {
|
||||
// There isn't enough data to digest a block, so append it to the buffer.
|
||||
ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
|
||||
ctx.buflen += input.len();
|
||||
self.store_context(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable multiple DMA transfers.
|
||||
T::regs().cr().modify(|w| w.set_mdmat(true));
|
||||
|
||||
let mut ilen_remaining = input.len();
|
||||
let mut input_start = 0;
|
||||
|
||||
// First ingest the data in the buffer.
|
||||
let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
|
||||
if empty_len > 0 {
|
||||
let copy_len = min(empty_len, ilen_remaining);
|
||||
ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
|
||||
ctx.buflen += copy_len;
|
||||
ilen_remaining -= copy_len;
|
||||
input_start += copy_len;
|
||||
}
|
||||
self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await;
|
||||
ctx.buflen = 0;
|
||||
|
||||
// Move any extra data to the now-empty buffer.
|
||||
let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
|
||||
if leftovers > 0 {
|
||||
assert!(ilen_remaining >= leftovers);
|
||||
ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
|
||||
ctx.buflen += leftovers;
|
||||
ilen_remaining -= leftovers;
|
||||
} else {
|
||||
ctx.buffer
|
||||
.copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
|
||||
ctx.buflen += DIGEST_BLOCK_SIZE;
|
||||
ilen_remaining -= DIGEST_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// Hash the remaining data.
|
||||
self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
|
||||
|
||||
// Save the peripheral context.
|
||||
self.store_context(ctx);
|
||||
}
|
||||
|
||||
/// Computes a digest for the given context.
|
||||
/// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
|
||||
/// The largest returned digest size is 128 bytes for SHA-512.
|
||||
/// Panics if the supplied digest buffer is too short.
|
||||
pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize {
|
||||
// Restore the peripheral state.
|
||||
self.load_context(&ctx);
|
||||
|
||||
// Hash the leftover bytes, if any.
|
||||
self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]);
|
||||
ctx.buflen = 0;
|
||||
|
||||
//Start the digest calculation.
|
||||
T::regs().str().write(|w| w.set_dcal(true));
|
||||
|
||||
// Load the HMAC key if provided.
|
||||
if let Some(key) = ctx.key {
|
||||
while !T::regs().sr().read().dinis() {}
|
||||
self.accumulate_blocking(key);
|
||||
T::regs().str().write(|w| w.set_dcal(true));
|
||||
}
|
||||
|
||||
// Block until digest computation is complete.
|
||||
while !T::regs().sr().read().dcis() {}
|
||||
|
||||
// Return the digest.
|
||||
let digest_words = match ctx.algo {
|
||||
Algorithm::SHA1 => 5,
|
||||
#[cfg(any(hash_v1, hash_v2, hash_v4))]
|
||||
Algorithm::MD5 => 4,
|
||||
Algorithm::SHA224 => 7,
|
||||
Algorithm::SHA256 => 8,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA384 => 12,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512_224 => 7,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512_256 => 8,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512 => 16,
|
||||
};
|
||||
|
||||
let digest_len_bytes = digest_words * 4;
|
||||
// Panics if the supplied digest buffer is too short.
|
||||
if digest.len() < digest_len_bytes {
|
||||
panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < digest_words {
|
||||
let word = T::regs().hr(i).read();
|
||||
digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
|
||||
i += 1;
|
||||
}
|
||||
digest_len_bytes
|
||||
}
|
||||
|
||||
/// Computes a digest for the given context.
|
||||
/// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
|
||||
/// The largest returned digest size is 128 bytes for SHA-512.
|
||||
/// Panics if the supplied digest buffer is too short.
|
||||
#[cfg(hash_v2)]
|
||||
pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize
|
||||
where
|
||||
D: crate::hash::Dma<T>,
|
||||
{
|
||||
// Restore the peripheral state.
|
||||
self.load_context(&ctx);
|
||||
|
||||
// Must be cleared prior to the last DMA transfer.
|
||||
T::regs().cr().modify(|w| w.set_mdmat(false));
|
||||
|
||||
// Hash the leftover bytes, if any.
|
||||
self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
|
||||
ctx.buflen = 0;
|
||||
|
||||
// Load the HMAC key if provided.
|
||||
if let Some(key) = ctx.key {
|
||||
self.accumulate(key).await;
|
||||
}
|
||||
|
||||
// Wait for completion.
|
||||
poll_fn(|cx| {
|
||||
// Check if already done.
|
||||
let bits = T::regs().sr().read();
|
||||
if bits.dcis() {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
// Register waker, then enable interrupts.
|
||||
HASH_WAKER.register(cx.waker());
|
||||
T::regs().imr().modify(|reg| reg.set_dcie(true));
|
||||
// Check for completion.
|
||||
let bits = T::regs().sr().read();
|
||||
if bits.dcis() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Return the digest.
|
||||
let digest_words = match ctx.algo {
|
||||
Algorithm::SHA1 => 5,
|
||||
#[cfg(any(hash_v1, hash_v2, hash_v4))]
|
||||
Algorithm::MD5 => 4,
|
||||
Algorithm::SHA224 => 7,
|
||||
Algorithm::SHA256 => 8,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA384 => 12,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512_224 => 7,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512_256 => 8,
|
||||
#[cfg(hash_v3)]
|
||||
Algorithm::SHA512 => 16,
|
||||
};
|
||||
|
||||
let digest_len_bytes = digest_words * 4;
|
||||
// Panics if the supplied digest buffer is too short.
|
||||
if digest.len() < digest_len_bytes {
|
||||
panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < digest_words {
|
||||
let word = T::regs().hr(i).read();
|
||||
digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
|
||||
i += 1;
|
||||
}
|
||||
digest_len_bytes
|
||||
}
|
||||
|
||||
/// Push data into the hash core.
|
||||
fn accumulate_blocking(&mut self, input: &[u8]) {
|
||||
// Set the number of valid bits.
|
||||
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
|
||||
T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
|
||||
|
||||
let mut i = 0;
|
||||
while i < input.len() {
|
||||
let mut word: [u8; 4] = [0; 4];
|
||||
let copy_idx = min(i + 4, input.len());
|
||||
word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]);
|
||||
T::regs().din().write_value(u32::from_ne_bytes(word));
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/// Push data into the hash core.
|
||||
#[cfg(hash_v2)]
|
||||
async fn accumulate(&mut self, input: &[u8])
|
||||
where
|
||||
D: crate::hash::Dma<T>,
|
||||
{
|
||||
// Ignore an input length of 0.
|
||||
if input.len() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the number of valid bits.
|
||||
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
|
||||
T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
|
||||
|
||||
// Configure DMA to transfer input to hash core.
|
||||
let dma_request = self.dma.request();
|
||||
let dst_ptr = T::regs().din().as_ptr();
|
||||
let mut num_words = input.len() / 4;
|
||||
if input.len() % 4 > 0 {
|
||||
num_words += 1;
|
||||
}
|
||||
let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
|
||||
let dma_transfer =
|
||||
unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
|
||||
T::regs().cr().modify(|w| w.set_dmae(true));
|
||||
|
||||
// Wait for the transfer to complete.
|
||||
dma_transfer.await;
|
||||
}
|
||||
|
||||
/// Save the peripheral state to a context.
|
||||
fn store_context<'c>(&mut self, ctx: &mut Context<'c>) {
|
||||
// Block waiting for data in ready.
|
||||
while !T::regs().sr().read().dinis() {}
|
||||
|
||||
// Store peripheral context.
|
||||
ctx.imr = T::regs().imr().read().0;
|
||||
ctx.str = T::regs().str().read().0;
|
||||
ctx.cr = T::regs().cr().read().0;
|
||||
let mut i = 0;
|
||||
while i < NUM_CONTEXT_REGS {
|
||||
ctx.csr[i] = T::regs().csr(i).read();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore the peripheral state from a context.
|
||||
fn load_context(&mut self, ctx: &Context) {
|
||||
// Restore the peripheral state from the context.
|
||||
T::regs().imr().write_value(Imr { 0: ctx.imr });
|
||||
T::regs().str().write_value(Str { 0: ctx.str });
|
||||
T::regs().cr().write_value(Cr { 0: ctx.cr });
|
||||
T::regs().cr().modify(|w| w.set_init(true));
|
||||
let mut i = 0;
|
||||
while i < NUM_CONTEXT_REGS {
|
||||
T::regs().csr(i).write_value(ctx.csr[i]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
fn regs() -> pac::hash::Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// HASH instance trait.
|
||||
pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
|
||||
/// Interrupt for this HASH instance.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
foreach_interrupt!(
|
||||
($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
|
||||
impl Instance for peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::hash::Hash {
|
||||
crate::pac::$inst
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
dma_trait!(Dma, Instance);
|
@ -45,6 +45,8 @@ pub mod exti;
|
||||
pub mod flash;
|
||||
#[cfg(fmc)]
|
||||
pub mod fmc;
|
||||
#[cfg(hash)]
|
||||
pub mod hash;
|
||||
#[cfg(hrtim)]
|
||||
pub mod hrtim;
|
||||
#[cfg(i2c)]
|
||||
|
@ -1,196 +0,0 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
/// Configuration of the clocks
|
||||
///
|
||||
/// hse takes precedence over hsi48 if both are enabled
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hse: Option<Hertz>,
|
||||
pub bypass_hse: bool,
|
||||
pub usb_pll: bool,
|
||||
|
||||
#[cfg(crs)]
|
||||
pub hsi48: Option<super::Hsi48Config>,
|
||||
|
||||
pub sys_ck: Option<Hertz>,
|
||||
pub hclk: Option<Hertz>,
|
||||
pub pclk: Option<Hertz>,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hse: Default::default(),
|
||||
bypass_hse: Default::default(),
|
||||
usb_pll: Default::default(),
|
||||
#[cfg(crs)]
|
||||
hsi48: Some(Default::default()),
|
||||
sys_ck: Default::default(),
|
||||
hclk: Default::default(),
|
||||
pclk: Default::default(),
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI_FREQ.0);
|
||||
|
||||
#[cfg(crs)]
|
||||
let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));
|
||||
#[cfg(not(crs))]
|
||||
let hsi48: Option<Hertz> = None;
|
||||
|
||||
let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| {
|
||||
if hsi48.is_some() {
|
||||
return (48_000_000, true);
|
||||
}
|
||||
(HSI_FREQ.0, false)
|
||||
});
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if sysclk == src_clk {
|
||||
(None, sysclk)
|
||||
} else {
|
||||
let prediv = if config.hse.is_some() { 1 } else { 2 };
|
||||
let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2;
|
||||
let pllmul = pllmul.max(2).min(16);
|
||||
|
||||
let pllmul_bits = pllmul as u8 - 2;
|
||||
let real_sysclk = pllmul * src_clk / prediv;
|
||||
(Some(pllmul_bits), real_sysclk)
|
||||
};
|
||||
|
||||
let hpre_bits = config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
let hclk = real_sysclk / (1 << (hpre_bits - 0b0111));
|
||||
|
||||
let ppre_bits = config
|
||||
.pclk
|
||||
.map(|pclk| match hclk / pclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre: u8 = 1 << (ppre_bits - 0b011);
|
||||
let pclk = hclk / u32::from(ppre);
|
||||
|
||||
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else {
|
||||
Latency::WS1
|
||||
});
|
||||
});
|
||||
|
||||
match (config.hse.is_some(), use_hsi48) {
|
||||
(true, _) => {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
w.set_hsebyp(config.bypass_hse);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
// use_hsi48 will always be false for stm32f0x0
|
||||
#[cfg(not(stm32f0x0))]
|
||||
(false, true) => {
|
||||
RCC.cr2().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.cr2().read().hsi48rdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if config.usb_pll {
|
||||
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P));
|
||||
}
|
||||
// TODO: Option to use CRS (Clock Recovery)
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits)));
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
w.set_sw(Sw::PLL1_P)
|
||||
});
|
||||
} else {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
|
||||
if config.hse.is_some() {
|
||||
w.set_sw(Sw::HSE);
|
||||
} else if use_hsi48 {
|
||||
#[cfg(not(stm32f0x0))]
|
||||
w.set_sw(Sw::HSI48);
|
||||
} else {
|
||||
w.set_sw(Sw::HSI)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_clocks!(
|
||||
hsi: None,
|
||||
lse: None,
|
||||
sys: Some(Hertz(real_sysclk)),
|
||||
pclk1: Some(Hertz(pclk)),
|
||||
pclk2: Some(Hertz(pclk)),
|
||||
pclk1_tim: Some(Hertz(pclk * timer_mul)),
|
||||
pclk2_tim: Some(Hertz(pclk * timer_mul)),
|
||||
hclk1: Some(Hertz(hclk)),
|
||||
rtc: rtc,
|
||||
hsi48: hsi48,
|
||||
|
||||
// TODO:
|
||||
pll1_p: None,
|
||||
);
|
||||
}
|
448
embassy-stm32/src/rcc/f013.rs
Normal file
448
embassy-stm32/src/rcc/f013.rs
Normal file
@ -0,0 +1,448 @@
|
||||
use crate::pac::flash::vals::Latency;
|
||||
#[cfg(stm32f1)]
|
||||
pub use crate::pac::rcc::vals::Adcpre as ADCPrescaler;
|
||||
#[cfg(stm32f3)]
|
||||
pub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler;
|
||||
use crate::pac::rcc::vals::Pllsrc;
|
||||
#[cfg(stm32f1)]
|
||||
pub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv;
|
||||
#[cfg(any(stm32f0, stm32f3))]
|
||||
pub use crate::pac::rcc::vals::Prediv as PllPreDiv;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1)
|
||||
Bypass,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum PllSource {
|
||||
HSE,
|
||||
HSI,
|
||||
#[cfg(rcc_f0v4)]
|
||||
HSI48,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Pll {
|
||||
pub src: PllSource,
|
||||
|
||||
/// PLL pre-divider.
|
||||
///
|
||||
/// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case.
|
||||
pub prediv: PllPreDiv,
|
||||
|
||||
/// PLL multiplication factor.
|
||||
pub mul: PllMul,
|
||||
}
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
Pll(AdcPllPrescaler),
|
||||
Hclk(AdcHclkPrescaler),
|
||||
}
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AdcHclkPrescaler {
|
||||
Div1,
|
||||
Div2,
|
||||
Div4,
|
||||
}
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HrtimClockSource {
|
||||
BusClk,
|
||||
PllClk,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hsi: bool,
|
||||
pub hse: Option<Hse>,
|
||||
#[cfg(crs)]
|
||||
pub hsi48: Option<super::Hsi48Config>,
|
||||
pub sys: Sysclk,
|
||||
|
||||
pub pll: Option<Pll>,
|
||||
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
#[cfg(not(stm32f0))]
|
||||
pub apb2_pre: APBPrescaler,
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
pub adc_pre: ADCPrescaler,
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
pub adc: AdcClockSource,
|
||||
#[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
|
||||
pub adc34: AdcClockSource,
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: HrtimClockSource,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hsi: true,
|
||||
hse: None,
|
||||
#[cfg(crs)]
|
||||
hsi48: Some(Default::default()),
|
||||
sys: Sysclk::HSI,
|
||||
pll: None,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
#[cfg(not(stm32f0))]
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
ls: Default::default(),
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
// ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz)
|
||||
adc_pre: ADCPrescaler::DIV6,
|
||||
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),
|
||||
#[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
|
||||
adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: HrtimClockSource::BusClk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize and Set the clock frequencies
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
false => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
true => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(HSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
match hse.mode {
|
||||
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
|
||||
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
|
||||
}
|
||||
|
||||
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// configure HSI48
|
||||
#[cfg(crs)]
|
||||
let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));
|
||||
#[cfg(not(crs))]
|
||||
let hsi48: Option<Hertz> = None;
|
||||
|
||||
// Enable PLL
|
||||
let pll = config.pll.map(|pll| {
|
||||
let (src_val, src_freq) = match pll.src {
|
||||
#[cfg(any(rcc_f0v3, rcc_f0v4, rcc_f3v3))]
|
||||
PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)),
|
||||
#[cfg(not(any(rcc_f0v3, rcc_f0v4, rcc_f3v3)))]
|
||||
PllSource::HSI => {
|
||||
if pll.prediv != PllPreDiv::DIV2 {
|
||||
panic!("if PLL source is HSI, PLL prediv must be 2.");
|
||||
}
|
||||
(Pllsrc::HSI_DIV2, unwrap!(hsi))
|
||||
}
|
||||
PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)),
|
||||
#[cfg(rcc_f0v4)]
|
||||
PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)),
|
||||
};
|
||||
let in_freq = src_freq / pll.prediv;
|
||||
assert!(max::PLL_IN.contains(&in_freq));
|
||||
let out_freq = in_freq * pll.mul;
|
||||
assert!(max::PLL_OUT.contains(&out_freq));
|
||||
|
||||
#[cfg(not(stm32f1))]
|
||||
RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv));
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(pll.mul);
|
||||
w.set_pllsrc(src_val);
|
||||
#[cfg(stm32f1)]
|
||||
w.set_pllxtpre(pll.prediv);
|
||||
});
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
out_freq
|
||||
});
|
||||
|
||||
#[cfg(any(rcc_f1, rcc_f1cl, stm32f3))]
|
||||
let usb = match pll {
|
||||
Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5),
|
||||
Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1),
|
||||
_ => None,
|
||||
}
|
||||
.map(|usbpre| {
|
||||
RCC.cfgr().modify(|w| w.set_usbpre(usbpre));
|
||||
Hertz(48_000_000)
|
||||
});
|
||||
|
||||
// Configure sysclk
|
||||
let sys = match config.sys {
|
||||
Sysclk::HSI => unwrap!(hsi),
|
||||
Sysclk::HSE => unwrap!(hse),
|
||||
Sysclk::PLL1_P => unwrap!(pll),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let hclk = sys / config.ahb_pre;
|
||||
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
|
||||
#[cfg(not(stm32f0))]
|
||||
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
|
||||
#[cfg(stm32f0)]
|
||||
let (pclk2, pclk2_tim) = (pclk1, pclk1_tim);
|
||||
|
||||
assert!(max::HCLK.contains(&hclk));
|
||||
assert!(max::PCLK1.contains(&pclk1));
|
||||
#[cfg(not(stm32f0))]
|
||||
assert!(max::PCLK2.contains(&pclk2));
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
let adc = pclk2 / config.adc_pre;
|
||||
#[cfg(stm32f1)]
|
||||
assert!(max::ADC.contains(&adc));
|
||||
|
||||
// Set latency based on HCLK frquency
|
||||
#[cfg(stm32f0)]
|
||||
let latency = match hclk.0 {
|
||||
..=24_000_000 => Latency::WS0,
|
||||
_ => Latency::WS1,
|
||||
};
|
||||
#[cfg(any(stm32f1, stm32f3))]
|
||||
let latency = match hclk.0 {
|
||||
..=24_000_000 => Latency::WS0,
|
||||
..=48_000_000 => Latency::WS1,
|
||||
_ => Latency::WS2,
|
||||
};
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(latency);
|
||||
// RM0316: "The prefetch buffer must be kept on when using a prescaler
|
||||
// different from 1 on the AHB clock.", "Half-cycle access cannot be
|
||||
// used when there is a prescaler different from 1 on the AHB clock"
|
||||
#[cfg(stm32f3)]
|
||||
if config.ahb_pre != AHBPrescaler::DIV1 {
|
||||
w.set_hlfcya(false);
|
||||
w.set_prftbe(true);
|
||||
}
|
||||
#[cfg(not(stm32f3))]
|
||||
w.set_prftbe(true);
|
||||
});
|
||||
|
||||
// Set prescalers
|
||||
// CFGR has been written before (PLL, PLL48) don't overwrite these settings
|
||||
RCC.cfgr().modify(|w: &mut stm32_metapac::rcc::regs::Cfgr| {
|
||||
#[cfg(not(stm32f0))]
|
||||
{
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
}
|
||||
#[cfg(stm32f0)]
|
||||
w.set_ppre(config.apb1_pre);
|
||||
w.set_hpre(config.ahb_pre);
|
||||
#[cfg(stm32f1)]
|
||||
w.set_adcpre(config.adc_pre);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from
|
||||
// 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
// CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
|
||||
RCC.cfgr().modify(|w| w.set_sw(config.sys));
|
||||
while RCC.cfgr().read().sws() != config.sys {}
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
use crate::pac::adccommon::vals::Ckmode;
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
let adc = match config.adc {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| w.set_adc12pres(adcpres));
|
||||
crate::pac::ADC_COMMON
|
||||
.ccr()
|
||||
.modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));
|
||||
|
||||
unwrap!(pll) / adcpres
|
||||
}
|
||||
AdcClockSource::Hclk(adcpres) => {
|
||||
assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));
|
||||
|
||||
let (div, ckmode) = match adcpres {
|
||||
AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1),
|
||||
AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2),
|
||||
AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4),
|
||||
};
|
||||
crate::pac::ADC_COMMON.ccr().modify(|w| w.set_ckmode(ckmode));
|
||||
|
||||
hclk / div
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
|
||||
let adc34 = match config.adc34 {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| w.set_adc34pres(adcpres));
|
||||
crate::pac::ADC3_COMMON
|
||||
.ccr()
|
||||
.modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));
|
||||
|
||||
unwrap!(pll) / adcpres
|
||||
}
|
||||
AdcClockSource::Hclk(adcpres) => {
|
||||
assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));
|
||||
|
||||
let (div, ckmode) = match adcpres {
|
||||
AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1),
|
||||
AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2),
|
||||
AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4),
|
||||
};
|
||||
crate::pac::ADC3_COMMON.ccr().modify(|w| w.set_ckmode(ckmode));
|
||||
|
||||
hclk / div
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
let hrtim = match config.hrtim {
|
||||
// Must be configured after the bus is ready, otherwise it won't work
|
||||
HrtimClockSource::BusClk => None,
|
||||
HrtimClockSource::PllClk => {
|
||||
use crate::pac::rcc::vals::Timsw;
|
||||
|
||||
// Make sure that we're using the PLL
|
||||
let pll = unwrap!(pll);
|
||||
assert!((pclk2 == pll) || (pclk2 * 2u32 == pll));
|
||||
|
||||
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
|
||||
|
||||
Some(pll * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_clocks!(
|
||||
hsi: hsi,
|
||||
hse: hse,
|
||||
pll1_p: pll,
|
||||
sys: Some(sys),
|
||||
pclk1: Some(pclk1),
|
||||
pclk2: Some(pclk2),
|
||||
pclk1_tim: Some(pclk1_tim),
|
||||
pclk2_tim: Some(pclk2_tim),
|
||||
hclk1: Some(hclk),
|
||||
#[cfg(all(stm32f3, not(rcc_f37)))]
|
||||
adc: Some(adc),
|
||||
#[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
|
||||
adc34: Some(adc34),
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: hrtim,
|
||||
rtc: rtc,
|
||||
hsi48: hsi48,
|
||||
#[cfg(any(rcc_f1, rcc_f1cl, stm32f3))]
|
||||
usb: usb,
|
||||
lse: None,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(stm32f0)]
|
||||
mod max {
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::time::Hertz;
|
||||
|
||||
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);
|
||||
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);
|
||||
|
||||
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
|
||||
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
|
||||
|
||||
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);
|
||||
pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(48_000_000);
|
||||
}
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
mod max {
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::time::Hertz;
|
||||
|
||||
#[cfg(not(rcc_f1cl))]
|
||||
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(16_000_000);
|
||||
#[cfg(not(rcc_f1cl))]
|
||||
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);
|
||||
|
||||
#[cfg(rcc_f1cl)]
|
||||
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(3_000_000)..=Hertz(25_000_000);
|
||||
#[cfg(rcc_f1cl)]
|
||||
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);
|
||||
|
||||
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
|
||||
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);
|
||||
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
|
||||
|
||||
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);
|
||||
pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);
|
||||
|
||||
pub(crate) const ADC: RangeInclusive<Hertz> = Hertz(0)..=Hertz(14_000_000);
|
||||
}
|
||||
|
||||
#[cfg(stm32f3)]
|
||||
mod max {
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::time::Hertz;
|
||||
|
||||
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);
|
||||
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);
|
||||
|
||||
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
|
||||
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);
|
||||
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
|
||||
|
||||
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);
|
||||
pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);
|
||||
}
|
@ -1,191 +0,0 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::*;
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
/// Configuration of the clocks
|
||||
///
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
pub hse: Option<Hertz>,
|
||||
|
||||
pub sys_ck: Option<Hertz>,
|
||||
pub hclk: Option<Hertz>,
|
||||
pub pclk1: Option<Hertz>,
|
||||
pub pclk2: Option<Hertz>,
|
||||
pub adcclk: Option<Hertz>,
|
||||
pub pllxtpre: bool,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let pllxtpre_div = if config.pllxtpre { 2 } else { 1 };
|
||||
let pllsrcclk = config.hse.map(|hse| hse.0 / pllxtpre_div).unwrap_or(HSI_FREQ.0 / 2);
|
||||
|
||||
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let pllmul = sysclk / pllsrcclk;
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if pllmul == 1 {
|
||||
(None, config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0))
|
||||
} else {
|
||||
let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16);
|
||||
(Some(pllmul as u8 - 2), pllsrcclk * pllmul)
|
||||
};
|
||||
|
||||
assert!(real_sysclk <= 72_000_000);
|
||||
|
||||
let hpre_bits = config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
|
||||
let hclk = if hpre_bits >= 0b1100 {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0110))
|
||||
} else {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0111))
|
||||
};
|
||||
|
||||
assert!(hclk <= 72_000_000);
|
||||
|
||||
let ppre1_bits = config
|
||||
.pclk1
|
||||
.map(|pclk1| match hclk / pclk1.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre1 = 1 << (ppre1_bits - 0b011);
|
||||
let pclk1 = hclk / u32::try_from(ppre1).unwrap();
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
|
||||
let ppre2_bits = config
|
||||
.pclk2
|
||||
.map(|pclk2| match hclk / pclk2.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre2 = 1 << (ppre2_bits - 0b011);
|
||||
let pclk2 = hclk / u32::try_from(ppre2).unwrap();
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
});
|
||||
// the prefetch buffer is enabled by default, let's keep it enabled
|
||||
w.set_prftbe(true);
|
||||
});
|
||||
|
||||
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
|
||||
// PLL output frequency is a supported one.
|
||||
// usbpre == false: divide clock by 1.5, otherwise no division
|
||||
#[cfg(not(rcc_f100))]
|
||||
let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) {
|
||||
(Some(_), Some(_), 72_000_000) => (false, true),
|
||||
(Some(_), Some(_), 48_000_000) => (true, true),
|
||||
_ => (true, false),
|
||||
};
|
||||
|
||||
let apre_bits: u8 = config
|
||||
.adcclk
|
||||
.map(|adcclk| match pclk2 / adcclk.0 {
|
||||
0..=2 => 0b00,
|
||||
3..=4 => 0b01,
|
||||
5..=7 => 0b10,
|
||||
_ => 0b11,
|
||||
})
|
||||
.unwrap_or(0b11);
|
||||
|
||||
let apre = (apre_bits + 1) << 1;
|
||||
let adcclk = pclk2 / unwrap!(u32::try_from(apre));
|
||||
|
||||
assert!(adcclk <= 14_000_000);
|
||||
|
||||
if config.hse.is_some() {
|
||||
// enable HSE and wait for it to be ready
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
|
||||
RCC.cfgr()
|
||||
.modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag)));
|
||||
|
||||
// enable PLL and wait for it to be ready
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(Pllmul::from_bits(pllmul_bits));
|
||||
w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre::from_bits(apre_bits));
|
||||
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
#[cfg(not(rcc_f100))]
|
||||
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||
w.set_sw(if pllmul_bits.is_some() {
|
||||
Sw::PLL1_P
|
||||
} else if config.hse.is_some() {
|
||||
Sw::HSE
|
||||
} else {
|
||||
Sw::HSI
|
||||
});
|
||||
});
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_clocks!(
|
||||
sys: Some(Hertz(real_sysclk)),
|
||||
pclk1: Some(Hertz(pclk1)),
|
||||
pclk2: Some(Hertz(pclk2)),
|
||||
pclk1_tim: Some(Hertz(pclk1 * timer_mul1)),
|
||||
pclk2_tim: Some(Hertz(pclk2 * timer_mul2)),
|
||||
hclk1: Some(Hertz(hclk)),
|
||||
adc: Some(Hertz(adcclk)),
|
||||
rtc: rtc,
|
||||
);
|
||||
}
|
@ -1,461 +0,0 @@
|
||||
#[cfg(rcc_f3)]
|
||||
use crate::pac::adccommon::vals::Ckmode;
|
||||
use crate::pac::flash::vals::Latency;
|
||||
pub use crate::pac::rcc::vals::Adcpres;
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
impl From<AdcClockSource> for Ckmode {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1,
|
||||
AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2,
|
||||
AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
Pll(Adcpres),
|
||||
BusDiv1,
|
||||
BusDiv2,
|
||||
BusDiv4,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn bus_div(&self) -> u32 {
|
||||
match self {
|
||||
Self::BusDiv1 => 1,
|
||||
Self::BusDiv2 => 2,
|
||||
Self::BusDiv4 => 4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum HrtimClockSource {
|
||||
#[default]
|
||||
BusClk,
|
||||
PllClk,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
/// Frequency of HSE oscillator
|
||||
/// 4MHz to 32MHz
|
||||
pub hse: Option<Hertz>,
|
||||
/// Bypass HSE for an external clock
|
||||
pub bypass_hse: bool,
|
||||
/// Frequency of the System Clock
|
||||
pub sysclk: Option<Hertz>,
|
||||
/// Frequency of AHB bus
|
||||
pub hclk: Option<Hertz>,
|
||||
/// Frequency of APB1 bus
|
||||
/// - Max frequency 36MHz
|
||||
pub pclk1: Option<Hertz>,
|
||||
/// Frequency of APB2 bus
|
||||
/// - Max frequency with HSE is 72MHz
|
||||
/// - Max frequency without HSE is 64MHz
|
||||
pub pclk2: Option<Hertz>,
|
||||
/// USB clock setup
|
||||
/// It is valid only when,
|
||||
/// - HSE is enabled,
|
||||
/// - The System clock frequency is either 48MHz or 72MHz
|
||||
/// - APB1 clock has a minimum frequency of 10MHz
|
||||
pub pll48: bool,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc: Option<AdcClockSource>,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc34: Option<AdcClockSource>,
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: HrtimClockSource,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
// Information required to setup the PLL clock
|
||||
#[derive(Clone, Copy)]
|
||||
struct PllConfig {
|
||||
pll_src: Pllsrc,
|
||||
pll_mul: Pllmul,
|
||||
pll_div: Option<Prediv>,
|
||||
}
|
||||
|
||||
/// Initialize and Set the clock frequencies
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Calculate the real System clock, and PLL configuration if applicable
|
||||
let (sysclk, pll_config) = get_sysclk(&config);
|
||||
assert!(sysclk.0 <= 72_000_000);
|
||||
|
||||
// Calculate real AHB clock
|
||||
let hclk = config.hclk.map(|h| h).unwrap_or(sysclk);
|
||||
let hpre = match sysclk.0 / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => Hpre::DIV1,
|
||||
2 => Hpre::DIV2,
|
||||
3..=5 => Hpre::DIV4,
|
||||
6..=11 => Hpre::DIV8,
|
||||
12..=39 => Hpre::DIV16,
|
||||
40..=95 => Hpre::DIV64,
|
||||
96..=191 => Hpre::DIV128,
|
||||
192..=383 => Hpre::DIV256,
|
||||
_ => Hpre::DIV512,
|
||||
};
|
||||
let hclk = sysclk / hpre;
|
||||
assert!(hclk <= Hertz(72_000_000));
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = config.pclk1.unwrap_or(hclk);
|
||||
let ppre1 = match hclk / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => Ppre::DIV1,
|
||||
2 => Ppre::DIV2,
|
||||
3..=5 => Ppre::DIV4,
|
||||
6..=11 => Ppre::DIV8,
|
||||
_ => Ppre::DIV16,
|
||||
};
|
||||
let timer_mul1 = if ppre1 == Ppre::DIV1 { 1u32 } else { 2 };
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= Hertz(36_000_000));
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = config.pclk2.unwrap_or(hclk);
|
||||
let ppre2 = match hclk / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => Ppre::DIV1,
|
||||
2 => Ppre::DIV2,
|
||||
3..=5 => Ppre::DIV4,
|
||||
6..=11 => Ppre::DIV8,
|
||||
_ => Ppre::DIV16,
|
||||
};
|
||||
let timer_mul2 = if ppre2 == Ppre::DIV1 { 1u32 } else { 2 };
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= Hertz(72_000_000));
|
||||
|
||||
// Set latency based on HCLK frquency
|
||||
// RM0316: "The prefetch buffer must be kept on when using a prescaler
|
||||
// different from 1 on the AHB clock.", "Half-cycle access cannot be
|
||||
// used when there is a prescaler different from 1 on the AHB clock"
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(if hclk <= Hertz(24_000_000) {
|
||||
Latency::WS0
|
||||
} else if hclk <= Hertz(48_000_000) {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
});
|
||||
if hpre != Hpre::DIV1 {
|
||||
w.set_hlfcya(false);
|
||||
w.set_prftbe(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Enable HSE
|
||||
// RM0316: "Bits 31:26 Reserved, must be kept at reset value."
|
||||
if config.hse.is_some() {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(config.bypass_hse);
|
||||
// We turn on clock security to switch to HSI when HSE fails
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
// RM0316: "Reserved, must be kept at reset value."
|
||||
if let Some(ref pll_config) = pll_config {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(pll_config.pll_mul);
|
||||
w.set_pllsrc(pll_config.pll_src);
|
||||
});
|
||||
if let Some(pll_div) = pll_config.pll_div {
|
||||
RCC.cfgr2().modify(|w| w.set_prediv(pll_div));
|
||||
}
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
// CFGR has been written before (PLL) don't overwrite these settings
|
||||
if config.pll48 {
|
||||
let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config);
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_usbpre(usb_pre);
|
||||
});
|
||||
}
|
||||
|
||||
// Set prescalers
|
||||
// CFGR has been written before (PLL, PLL48) don't overwrite these settings
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(ppre2);
|
||||
w.set_ppre1(ppre1);
|
||||
w.set_hpre(hpre);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from
|
||||
// 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
// CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(match (pll_config, config.hse) {
|
||||
(Some(_), _) => Sw::PLL1_P,
|
||||
(None, Some(_)) => Sw::HSE,
|
||||
(None, None) => Sw::HSI,
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc = config.adc.map(|adc| match adc {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adcpres);
|
||||
|
||||
sysclk / adcpres
|
||||
})
|
||||
}
|
||||
_ => crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
sysclk / adc.bus_div()
|
||||
}),
|
||||
});
|
||||
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
let adc34 = config.adc34.map(|adc| match adc {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc34pres(adcpres);
|
||||
|
||||
sysclk / adcpres
|
||||
})
|
||||
}
|
||||
_ => crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
sysclk / adc.bus_div()
|
||||
}),
|
||||
});
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
let hrtim = match config.hrtim {
|
||||
// Must be configured after the bus is ready, otherwise it won't work
|
||||
HrtimClockSource::BusClk => None,
|
||||
HrtimClockSource::PllClk => {
|
||||
use crate::pac::rcc::vals::Timsw;
|
||||
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
|
||||
|
||||
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
|
||||
|
||||
Some(sysclk * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_clocks!(
|
||||
hsi: None,
|
||||
lse: None,
|
||||
pll1_p: None,
|
||||
sys: Some(sysclk),
|
||||
pclk1: Some(pclk1),
|
||||
pclk2: Some(pclk2),
|
||||
pclk1_tim: Some(pclk1 * timer_mul1),
|
||||
pclk2_tim: Some(pclk2 * timer_mul2),
|
||||
hclk1: Some(hclk),
|
||||
#[cfg(rcc_f3)]
|
||||
adc: adc,
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
adc34: adc34,
|
||||
#[cfg(all(rcc_f3, not(adc3_common)))]
|
||||
adc34: None,
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: hrtim,
|
||||
rtc: rtc,
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sysclk(config: &Config) -> (Hertz, Option<PllConfig>) {
|
||||
match (config.sysclk, config.hse) {
|
||||
(Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
|
||||
(Some(sysclk), None) if sysclk == HSI_FREQ => (HSI_FREQ, None),
|
||||
// If the user selected System clock is different from HSI or HSE
|
||||
// we will have to setup PLL clock source
|
||||
(Some(sysclk), _) => {
|
||||
let (sysclk, pll_config) = calc_pll(config, sysclk);
|
||||
(sysclk, Some(pll_config))
|
||||
}
|
||||
(None, Some(hse)) => (hse, None),
|
||||
(None, None) => (HSI_FREQ, None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||
// Calculates the Multiplier and the Divisor to arrive at
|
||||
// the required System clock from PLL source frequency
|
||||
let get_mul_div = |sysclk, pllsrcclk| {
|
||||
let bus_div = gcd(sysclk, pllsrcclk);
|
||||
let mut multiplier = sysclk / bus_div;
|
||||
let mut divisor = pllsrcclk / bus_div;
|
||||
// Minimum PLL multiplier is two
|
||||
if multiplier == 1 {
|
||||
multiplier *= 2;
|
||||
divisor *= 2;
|
||||
}
|
||||
assert!(multiplier <= 16);
|
||||
assert!(divisor <= 16);
|
||||
(multiplier, divisor)
|
||||
};
|
||||
// Based on the source of Pll, we calculate the actual system clock
|
||||
// frequency, PLL's source identifier, multiplier and divisor
|
||||
let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse {
|
||||
Some(Hertz(hse)) => {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, hse);
|
||||
(
|
||||
Hertz((hse / divisor) * multiplier),
|
||||
Pllsrc::HSE_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
cfg_if::cfg_if! {
|
||||
// For some chips PREDIV is always two, and cannot be changed
|
||||
if #[cfg(any(flashsize_d, flashsize_e))] {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
|
||||
(
|
||||
Hertz((HSI_FREQ.0 / divisor) * multiplier),
|
||||
Pllsrc::HSI_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
} else {
|
||||
let pllsrcclk = HSI_FREQ.0 / 2;
|
||||
let multiplier = sysclk / pllsrcclk;
|
||||
assert!(multiplier <= 16);
|
||||
(
|
||||
Hertz(pllsrcclk * multiplier),
|
||||
Pllsrc::HSI_DIV2,
|
||||
into_pll_mul(multiplier),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
act_sysclk,
|
||||
PllConfig {
|
||||
pll_src,
|
||||
pll_mul,
|
||||
pll_div,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn get_usb_pre(config: &Config, sysclk: Hertz, pclk1: Hertz, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||
cfg_if::cfg_if! {
|
||||
// Some chips do not have USB
|
||||
if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
|
||||
panic!("USB clock not supported by the chip");
|
||||
} else {
|
||||
let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= Hertz(10_000_000));
|
||||
match (usb_ok, sysclk) {
|
||||
(true, Hertz(72_000_000)) => Usbpre::DIV1_5,
|
||||
(true, Hertz(48_000_000)) => Usbpre::DIV1,
|
||||
_ => panic!(
|
||||
"USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function assumes cases when multiplier is one and it
|
||||
// being greater than 16 is made impossible
|
||||
#[inline]
|
||||
fn into_pll_mul(multiplier: u32) -> Pllmul {
|
||||
match multiplier {
|
||||
2 => Pllmul::MUL2,
|
||||
3 => Pllmul::MUL3,
|
||||
4 => Pllmul::MUL4,
|
||||
5 => Pllmul::MUL5,
|
||||
6 => Pllmul::MUL6,
|
||||
7 => Pllmul::MUL7,
|
||||
8 => Pllmul::MUL8,
|
||||
9 => Pllmul::MUL9,
|
||||
10 => Pllmul::MUL10,
|
||||
11 => Pllmul::MUL11,
|
||||
12 => Pllmul::MUL12,
|
||||
13 => Pllmul::MUL13,
|
||||
14 => Pllmul::MUL14,
|
||||
15 => Pllmul::MUL15,
|
||||
16 => Pllmul::MUL16,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// This function assumes the incoming divisor cannot be greater
|
||||
// than 16
|
||||
#[inline]
|
||||
fn into_pre_div(divisor: u32) -> Prediv {
|
||||
match divisor {
|
||||
1 => Prediv::DIV1,
|
||||
2 => Prediv::DIV2,
|
||||
3 => Prediv::DIV3,
|
||||
4 => Prediv::DIV4,
|
||||
5 => Prediv::DIV5,
|
||||
6 => Prediv::DIV6,
|
||||
7 => Prediv::DIV7,
|
||||
8 => Prediv::DIV8,
|
||||
9 => Prediv::DIV9,
|
||||
10 => Prediv::DIV10,
|
||||
11 => Prediv::DIV11,
|
||||
12 => Prediv::DIV12,
|
||||
13 => Prediv::DIV13,
|
||||
14 => Prediv::DIV14,
|
||||
15 => Prediv::DIV15,
|
||||
16 => Prediv::DIV16,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Determine GCD using Euclidean algorithm
|
||||
#[inline]
|
||||
fn gcd(mut a: u32, mut b: u32) -> u32 {
|
||||
while b != 0 {
|
||||
let r = a % b;
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
a
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw};
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN,
|
||||
Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler,
|
||||
Adcsel as AdcClockSource, Clk48sel as Clk48Src, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler,
|
||||
Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler,
|
||||
Sw as Sysclk,
|
||||
};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::time::Hertz;
|
||||
@ -12,28 +13,22 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
HSE(Hertz),
|
||||
HSI,
|
||||
PLL,
|
||||
/// HSE Mode
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1)
|
||||
Bypass,
|
||||
}
|
||||
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PllSource {
|
||||
HSI,
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PllSource {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PllSource::HSE(..) => Pllsrc::HSE,
|
||||
PllSource::HSI => Pllsrc::HSI,
|
||||
}
|
||||
}
|
||||
/// HSE Configuration
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
/// PLL Configuration
|
||||
@ -43,69 +38,89 @@ impl Into<Pllsrc> for PllSource {
|
||||
/// frequency ranges for each of these settings.
|
||||
pub struct Pll {
|
||||
/// PLL Source clock selection.
|
||||
pub source: PllSource,
|
||||
pub source: Pllsrc,
|
||||
|
||||
/// PLL pre-divider
|
||||
pub prediv_m: PllM,
|
||||
pub prediv: PllPreDiv,
|
||||
|
||||
/// PLL multiplication factor for VCO
|
||||
pub mul_n: PllN,
|
||||
pub mul: PllMul,
|
||||
|
||||
/// PLL division factor for P clock (ADC Clock)
|
||||
pub div_p: Option<PllP>,
|
||||
pub divp: Option<PllPDiv>,
|
||||
|
||||
/// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI)
|
||||
pub div_q: Option<PllQ>,
|
||||
pub divq: Option<PllQDiv>,
|
||||
|
||||
/// PLL division factor for R clock (SYSCLK)
|
||||
pub div_r: Option<PllR>,
|
||||
}
|
||||
|
||||
/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
|
||||
pub enum Clock48MhzSrc {
|
||||
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||
/// oscillator to comply with the USB specification for oscillator tolerance.
|
||||
Hsi48(super::Hsi48Config),
|
||||
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
||||
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
||||
/// tolerance.
|
||||
PllQ,
|
||||
pub divr: Option<PllRDiv>,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
/// HSI Enable
|
||||
pub hsi: bool,
|
||||
|
||||
/// HSE Configuration
|
||||
pub hse: Option<Hse>,
|
||||
|
||||
/// System Clock Configuration
|
||||
pub sys: Sysclk,
|
||||
|
||||
/// HSI48 Configuration
|
||||
pub hsi48: Option<super::Hsi48Config>,
|
||||
|
||||
/// PLL Configuration
|
||||
pub pll: Option<Pll>,
|
||||
|
||||
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
||||
/// MUST turn on the PLLR output.
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
|
||||
pub low_power_run: bool,
|
||||
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
||||
/// MUST turn on the PLLR output.
|
||||
pub pll: Option<Pll>,
|
||||
|
||||
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||
pub clk48_src: Clk48Src,
|
||||
|
||||
/// Low-Speed Clock Configuration
|
||||
pub ls: super::LsConfig,
|
||||
|
||||
/// Clock Source for ADCs 1 and 2
|
||||
pub adc12_clock_source: AdcClockSource,
|
||||
|
||||
/// Clock Source for ADCs 3, 4 and 5
|
||||
pub adc345_clock_source: AdcClockSource,
|
||||
|
||||
/// Clock Source for FDCAN
|
||||
pub fdcan_clock_source: FdCanClockSource,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
/// Enable range1 boost mode
|
||||
/// Recommended when the SYSCLK frequency is greater than 150MHz.
|
||||
pub boost: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI,
|
||||
hsi: true,
|
||||
hse: None,
|
||||
sys: Sysclk::HSI,
|
||||
hsi48: Some(Default::default()),
|
||||
pll: None,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
low_power_run: false,
|
||||
pll: None,
|
||||
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())),
|
||||
clk48_src: Clk48Src::HSI48,
|
||||
ls: Default::default(),
|
||||
adc12_clock_source: Adcsel::DISABLE,
|
||||
adc345_clock_source: Adcsel::DISABLE,
|
||||
fdcan_clock_source: FdCanClockSource::PCLK1,
|
||||
ls: Default::default(),
|
||||
boost: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,34 +132,65 @@ pub struct PllFreq {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let pll_freq = config.pll.map(|pll_config| {
|
||||
let src_freq = match pll_config.source {
|
||||
PllSource::HSI => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
false => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
PllSource::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq
|
||||
true => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(HSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
match hse.mode {
|
||||
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
|
||||
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
|
||||
}
|
||||
|
||||
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSI48 if required
|
||||
if let Some(hsi48_config) = config.hsi48 {
|
||||
super::init_hsi48(hsi48_config);
|
||||
}
|
||||
|
||||
let pll_freq = config.pll.map(|pll_config| {
|
||||
let src_freq = match pll_config.source {
|
||||
Pllsrc::HSI => unwrap!(hsi),
|
||||
Pllsrc::HSE => unwrap!(hse),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// TODO: check PLL input, internal and output frequencies for validity
|
||||
|
||||
// Disable PLL before configuration
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n;
|
||||
let internal_freq = src_freq / pll_config.prediv * pll_config.mul;
|
||||
|
||||
RCC.pllcfgr().write(|w| {
|
||||
w.set_plln(pll_config.mul_n);
|
||||
w.set_pllm(pll_config.prediv_m);
|
||||
w.set_plln(pll_config.mul);
|
||||
w.set_pllm(pll_config.prediv);
|
||||
w.set_pllsrc(pll_config.source.into());
|
||||
});
|
||||
|
||||
let pll_p_freq = pll_config.div_p.map(|div_p| {
|
||||
let pll_p_freq = pll_config.divp.map(|div_p| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllp(div_p);
|
||||
w.set_pllpen(true);
|
||||
@ -152,7 +198,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
internal_freq / div_p
|
||||
});
|
||||
|
||||
let pll_q_freq = pll_config.div_q.map(|div_q| {
|
||||
let pll_q_freq = pll_config.divq.map(|div_q| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllq(div_q);
|
||||
w.set_pllqen(true);
|
||||
@ -160,7 +206,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
internal_freq / div_q
|
||||
});
|
||||
|
||||
let pll_r_freq = pll_config.div_r.map(|div_r| {
|
||||
let pll_r_freq = pll_config.divr.map(|div_r| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllr(div_r);
|
||||
w.set_pllren(true);
|
||||
@ -179,22 +225,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
});
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL => {
|
||||
let (sys_clk, sw) = match config.sys {
|
||||
Sysclk::HSI => (HSI_FREQ, Sw::HSI),
|
||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
||||
Sysclk::PLL1_R => {
|
||||
assert!(pll_freq.is_some());
|
||||
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
||||
|
||||
@ -202,41 +236,51 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
assert!(freq <= 170_000_000);
|
||||
|
||||
if freq >= 150_000_000 {
|
||||
// Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234)
|
||||
PWR.cr5().modify(|w| w.set_r1mode(false));
|
||||
// Set flash wait state in boost mode based on frequency ([RM0440] p191)
|
||||
if freq <= 36_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS0));
|
||||
} else if freq <= 68_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS1));
|
||||
} else if freq <= 102_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS2));
|
||||
} else if freq <= 136_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS3));
|
||||
} else {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS4));
|
||||
}
|
||||
} else {
|
||||
PWR.cr5().modify(|w| w.set_r1mode(true));
|
||||
// Set flash wait state in normal mode based on frequency ([RM0440] p191)
|
||||
if freq <= 30_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS0));
|
||||
} else if freq <= 60_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS1));
|
||||
} else if freq <= 80_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS2));
|
||||
} else if freq <= 120_000_000 {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS3));
|
||||
} else {
|
||||
FLASH.acr().modify(|w| w.set_latency(Latency::WS4));
|
||||
}
|
||||
}
|
||||
|
||||
(Hertz(freq), Sw::PLL1_R)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
|
||||
let hclk = sys_clk / config.ahb_pre;
|
||||
|
||||
// Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!)
|
||||
if config.boost {
|
||||
// RM0440 p235
|
||||
// “The sequence to switch from Range1 normal mode to Range1 boost mode is:
|
||||
// 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency.
|
||||
RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2));
|
||||
// 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode)
|
||||
PWR.cr5().modify(|w| w.set_r1mode(false));
|
||||
|
||||
// Below:
|
||||
// 3. Adjust wait states according to new freq target
|
||||
// 4. Configure and switch to new frequency
|
||||
}
|
||||
|
||||
// Configure flash read access latency based on boost mode and frequency (RM0440 p98)
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(match (config.boost, hclk.0) {
|
||||
(true, ..=34_000_000) => Latency::WS0,
|
||||
(true, ..=68_000_000) => Latency::WS1,
|
||||
(true, ..=102_000_000) => Latency::WS2,
|
||||
(true, ..=136_000_000) => Latency::WS3,
|
||||
(true, _) => Latency::WS4,
|
||||
|
||||
(false, ..=36_000_000) => Latency::WS0,
|
||||
(false, ..=60_000_000) => Latency::WS1,
|
||||
(false, ..=90_000_000) => Latency::WS2,
|
||||
(false, ..=120_000_000) => Latency::WS3,
|
||||
(false, _) => Latency::WS4,
|
||||
})
|
||||
});
|
||||
|
||||
if config.boost {
|
||||
// 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency.
|
||||
cortex_m::asm::delay(16);
|
||||
}
|
||||
|
||||
// Now that boost mode and flash read access latency are configured, set up SYSCLK
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre);
|
||||
@ -244,42 +288,26 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
});
|
||||
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
// Setup the 48 MHz clock if needed
|
||||
if let Some(clock_48mhz_src) = config.clock_48mhz_src {
|
||||
let source = match clock_48mhz_src {
|
||||
Clock48MhzSrc::PllQ => {
|
||||
// Make sure the PLLQ is enabled and running at 48Mhz
|
||||
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
||||
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
||||
let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre);
|
||||
let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre);
|
||||
|
||||
// Configure the 48MHz clock source for USB and RNG peripherals.
|
||||
RCC.ccipr().modify(|w| {
|
||||
w.set_clk48sel(match config.clk48_src {
|
||||
Clk48Src::PLL1_Q => {
|
||||
// Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock.
|
||||
// Peripherals which require one (USB, RNG) should check that they‘re driven by a valid 48MHz
|
||||
// clock at init.
|
||||
crate::pac::rcc::vals::Clk48sel::PLL1_Q
|
||||
}
|
||||
Clock48MhzSrc::Hsi48(config) => {
|
||||
super::init_hsi48(config);
|
||||
Clk48Src::HSI48 => {
|
||||
// Make sure HSI48 is enabled
|
||||
assert!(config.hsi48.is_some());
|
||||
crate::pac::rcc::vals::Clk48sel::HSI48
|
||||
}
|
||||
};
|
||||
|
||||
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
});
|
||||
|
||||
RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source));
|
||||
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
|
||||
@ -308,18 +336,42 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
set_clocks!(
|
||||
sys: Some(sys_clk),
|
||||
hclk1: Some(ahb_freq),
|
||||
hclk2: Some(ahb_freq),
|
||||
hclk3: Some(ahb_freq),
|
||||
hclk1: Some(hclk),
|
||||
hclk2: Some(hclk),
|
||||
hclk3: Some(hclk),
|
||||
pclk1: Some(apb1_freq),
|
||||
pclk1_tim: Some(apb1_tim_freq),
|
||||
pclk2: Some(apb2_freq),
|
||||
pclk2_tim: Some(apb2_tim_freq),
|
||||
adc: adc12_ck,
|
||||
adc34: adc345_ck,
|
||||
pll1_p: None,
|
||||
pll1_q: None, // TODO
|
||||
hse: None, // TODO
|
||||
pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p),
|
||||
pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_p),
|
||||
hse: hse,
|
||||
rtc: rtc,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: if necessary, make more of these, gated behind cfg attrs
|
||||
mod max {
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSE 4-48MHz (RM0440 p280)
|
||||
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);
|
||||
|
||||
/// External Clock ?-48MHz (RM0440 p280)
|
||||
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
|
||||
|
||||
// SYSCLK ?-170MHz (RM0440 p282)
|
||||
//pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);
|
||||
|
||||
// PLL Output frequency ?-170MHz (RM0440 p281)
|
||||
//pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);
|
||||
|
||||
// Left over from f.rs, remove if not necessary
|
||||
//pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
|
||||
//pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
|
||||
//pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
|
||||
}
|
||||
|
@ -170,22 +170,7 @@ pub enum SupplyConfig {
|
||||
/// This is only used in certain power supply configurations:
|
||||
/// SMPSLDO, SMPSExternalLDO, SMPSExternalLDOBypass.
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
#[derive(PartialEq)]
|
||||
pub enum SMPSSupplyVoltage {
|
||||
V1_8,
|
||||
V2_5,
|
||||
}
|
||||
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
impl SMPSSupplyVoltage {
|
||||
/// Convert SMPSSupplyVoltage to u8 representation.
|
||||
fn to_u8(&self) -> u8 {
|
||||
match self {
|
||||
SMPSSupplyVoltage::V1_8 => 0b01,
|
||||
SMPSSupplyVoltage::V2_5 => 0b10,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use pac::pwr::vals::Sdlevel as SMPSSupplyVoltage;
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
@ -279,7 +264,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
match config.supply_config {
|
||||
SupplyConfig::Default => {
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_sdlevel(0b00);
|
||||
w.set_sdlevel(SMPSSupplyVoltage::RESET);
|
||||
w.set_sdexthp(false);
|
||||
w.set_sden(true);
|
||||
w.set_ldoen(true);
|
||||
@ -301,11 +286,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_bypass(false);
|
||||
});
|
||||
}
|
||||
SupplyConfig::SMPSLDO(ref smps_supply_voltage)
|
||||
| SupplyConfig::SMPSExternalLDO(ref smps_supply_voltage)
|
||||
| SupplyConfig::SMPSExternalLDOBypass(ref smps_supply_voltage) => {
|
||||
SupplyConfig::SMPSLDO(smps_supply_voltage)
|
||||
| SupplyConfig::SMPSExternalLDO(smps_supply_voltage)
|
||||
| SupplyConfig::SMPSExternalLDOBypass(smps_supply_voltage) => {
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_sdlevel(smps_supply_voltage.to_u8());
|
||||
w.set_sdlevel(smps_supply_voltage);
|
||||
w.set_sdexthp(matches!(
|
||||
config.supply_config,
|
||||
SupplyConfig::SMPSExternalLDO(_) | SupplyConfig::SMPSExternalLDOBypass(_)
|
||||
|
@ -4,7 +4,7 @@ use embassy_hal_internal::into_ref;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
#[cfg(not(stm32f1))]
|
||||
#[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]
|
||||
pub use crate::pac::rcc::vals::Mcopre as McoPrescaler;
|
||||
#[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))]
|
||||
pub use crate::pac::rcc::vals::Mcosel as McoSource;
|
||||
@ -13,10 +13,16 @@ pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source};
|
||||
use crate::pac::RCC;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum McoPrescaler {
|
||||
DIV1,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: super::McoPrescaler);
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +35,7 @@ macro_rules! impl_peri {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: McoPrescaler) {
|
||||
unsafe fn apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) {
|
||||
#[cfg(not(any(stm32u5, stm32wba)))]
|
||||
let r = RCC.cfgr();
|
||||
#[cfg(any(stm32u5, stm32wba))]
|
||||
@ -37,8 +43,8 @@ macro_rules! impl_peri {
|
||||
|
||||
r.modify(|w| {
|
||||
w.$set_source(source);
|
||||
#[cfg(not(stm32f1))]
|
||||
w.$set_prescaler(prescaler);
|
||||
#[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]
|
||||
w.$set_prescaler(_prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -68,16 +74,12 @@ impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
source: T::Source,
|
||||
#[cfg(not(stm32f1))] prescaler: McoPrescaler,
|
||||
prescaler: McoPrescaler,
|
||||
) -> Self {
|
||||
into_ref!(pin);
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
T::apply_clock_settings(
|
||||
source,
|
||||
#[cfg(not(stm32f1))]
|
||||
prescaler,
|
||||
);
|
||||
T::apply_clock_settings(source, prescaler);
|
||||
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
|
||||
pin.set_speed(Speed::VeryHigh);
|
||||
});
|
||||
|
@ -18,17 +18,15 @@ mod hsi48;
|
||||
#[cfg(crs)]
|
||||
pub use hsi48::*;
|
||||
|
||||
#[cfg_attr(rcc_f0, path = "f0.rs")]
|
||||
#[cfg_attr(any(stm32f1), path = "f1.rs")]
|
||||
#[cfg_attr(any(stm32f3), path = "f3.rs")]
|
||||
#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")]
|
||||
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
||||
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
||||
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
||||
#[cfg_attr(any(stm32f0, stm32f1, stm32f3), path = "f013.rs")]
|
||||
#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f247.rs")]
|
||||
#[cfg_attr(stm32c0, path = "c0.rs")]
|
||||
#[cfg_attr(stm32g0, path = "g0.rs")]
|
||||
#[cfg_attr(stm32g4, path = "g4.rs")]
|
||||
#[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")]
|
||||
#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
|
||||
#[cfg_attr(rcc_u5, path = "u5.rs")]
|
||||
#[cfg_attr(rcc_wba, path = "wba.rs")]
|
||||
#[cfg_attr(stm32u5, path = "u5.rs")]
|
||||
#[cfg_attr(stm32wba, path = "wba.rs")]
|
||||
mod _version;
|
||||
|
||||
pub use _version::*;
|
||||
|
@ -49,7 +49,7 @@ impl Default for Config {
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb7_pre: APBPrescaler::DIV1,
|
||||
ls: Default::default(),
|
||||
adc_clock_source: AdcClockSource::HCLK1,
|
||||
adc_clock_source: AdcClockSource::HCLK4,
|
||||
voltage_scale: VoltageScale::RANGE2,
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryInto;
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
|
||||
@ -22,18 +24,22 @@ use crate::{interrupt, peripherals};
|
||||
// As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any
|
||||
// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST
|
||||
// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not
|
||||
// candidates for use as an embassy-time driver provider.
|
||||
// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.)
|
||||
//
|
||||
// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number
|
||||
// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers:
|
||||
// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3.
|
||||
|
||||
#[cfg(not(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22)))]
|
||||
const ALARM_COUNT: usize = 3;
|
||||
|
||||
#[cfg(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))]
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] {
|
||||
const ALARM_COUNT: usize = 1;
|
||||
} else {
|
||||
const ALARM_COUNT: usize = 3;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(time_drvier_tim1)]
|
||||
type T = peripherals::TIM1;
|
||||
#[cfg(time_driver_tim2)]
|
||||
type T = peripherals::TIM2;
|
||||
#[cfg(time_driver_tim3)]
|
||||
@ -42,6 +48,8 @@ type T = peripherals::TIM3;
|
||||
type T = peripherals::TIM4;
|
||||
#[cfg(time_driver_tim5)]
|
||||
type T = peripherals::TIM5;
|
||||
#[cfg(time_driver_tim8)]
|
||||
type T = peripherals::TIM8;
|
||||
#[cfg(time_driver_tim9)]
|
||||
type T = peripherals::TIM9;
|
||||
#[cfg(time_driver_tim11)]
|
||||
@ -50,12 +58,26 @@ type T = peripherals::TIM11;
|
||||
type T = peripherals::TIM12;
|
||||
#[cfg(time_driver_tim15)]
|
||||
type T = peripherals::TIM15;
|
||||
#[cfg(time_driver_tim20)]
|
||||
type T = peripherals::TIM20;
|
||||
#[cfg(time_driver_tim21)]
|
||||
type T = peripherals::TIM21;
|
||||
#[cfg(time_driver_tim22)]
|
||||
type T = peripherals::TIM22;
|
||||
#[cfg(time_driver_tim23)]
|
||||
type T = peripherals::TIM23;
|
||||
#[cfg(time_driver_tim24)]
|
||||
type T = peripherals::TIM24;
|
||||
|
||||
foreach_interrupt! {
|
||||
(TIM1, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim1)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM2, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim2)]
|
||||
#[cfg(feature = "rt")]
|
||||
@ -88,16 +110,16 @@ foreach_interrupt! {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM9, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim9)]
|
||||
(TIM8, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim8)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM11, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim11)]
|
||||
(TIM9, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim9)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
@ -120,6 +142,14 @@ foreach_interrupt! {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM20, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim20)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM21, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim21)]
|
||||
#[cfg(feature = "rt")]
|
||||
@ -136,6 +166,22 @@ foreach_interrupt! {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM23, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim23)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
(TIM24, timer, $block:ident, UP, $irq:ident) => {
|
||||
#[cfg(time_driver_tim24)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn $irq() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Clock timekeeping works with something we call "periods", which are time intervals
|
||||
|
@ -6,7 +6,7 @@ use core::fmt::Write as _;
|
||||
|
||||
use embassy_futures::join::join;
|
||||
use embassy_sync::pipe::Pipe;
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
|
||||
use embassy_usb::driver::Driver;
|
||||
use embassy_usb::{Builder, Config};
|
||||
use log::{Metadata, Record};
|
||||
@ -37,6 +37,9 @@ impl<'d> LoggerState<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The packet size used in the usb logger, to be used with `create_future_from_class`
|
||||
pub const MAX_PACKET_SIZE: u8 = 64;
|
||||
|
||||
/// The logger handle, which contains a pipe with configurable size for buffering log messages.
|
||||
pub struct UsbLogger<const N: usize> {
|
||||
buffer: Pipe<CS, N>,
|
||||
@ -54,7 +57,6 @@ impl<const N: usize> UsbLogger<N> {
|
||||
D: Driver<'d>,
|
||||
Self: 'd,
|
||||
{
|
||||
const MAX_PACKET_SIZE: u8 = 64;
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("USB-serial logger");
|
||||
@ -87,12 +89,24 @@ impl<const N: usize> UsbLogger<N> {
|
||||
let mut device = builder.build();
|
||||
loop {
|
||||
let run_fut = device.run();
|
||||
let class_fut = self.run_logger_class(&mut sender, &mut receiver);
|
||||
join(run_fut, class_fut).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_logger_class<'d, D>(&self, sender: &mut Sender<'d, D>, receiver: &mut Receiver<'d, D>)
|
||||
where
|
||||
D: Driver<'d>,
|
||||
{
|
||||
let log_fut = async {
|
||||
let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
|
||||
sender.wait_connection().await;
|
||||
loop {
|
||||
let len = self.buffer.read(&mut rx[..]).await;
|
||||
let _ = sender.write_packet(&rx[..len]).await;
|
||||
if len as u8 == MAX_PACKET_SIZE {
|
||||
let _ = sender.write_packet(&[]).await;
|
||||
}
|
||||
}
|
||||
};
|
||||
let discard_fut = async {
|
||||
@ -102,7 +116,19 @@ impl<const N: usize> UsbLogger<N> {
|
||||
let _ = receiver.read_packet(&mut discard_buf).await;
|
||||
}
|
||||
};
|
||||
join(run_fut, join(log_fut, discard_fut)).await;
|
||||
|
||||
join(log_fut, discard_fut).await;
|
||||
}
|
||||
|
||||
/// Creates the futures needed for the logger from a given class
|
||||
/// This can be used in cases where the usb device is already in use for another connection
|
||||
pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>)
|
||||
where
|
||||
D: Driver<'d>,
|
||||
{
|
||||
let (mut sender, mut receiver) = class.split();
|
||||
loop {
|
||||
self.run_logger_class(&mut sender, &mut receiver).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,3 +179,27 @@ macro_rules! run {
|
||||
let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
|
||||
};
|
||||
}
|
||||
|
||||
/// Initialize the USB serial logger from a serial class and return the future to run it.
|
||||
///
|
||||
/// Arguments specify the buffer size, log level and the serial class, respectively.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```
|
||||
/// embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, class);
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This macro should only be invoked only once since it is setting the global logging state of the application.
|
||||
#[macro_export]
|
||||
macro_rules! with_class {
|
||||
( $x:expr, $l:expr, $p:ident ) => {{
|
||||
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
|
||||
unsafe {
|
||||
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
|
||||
}
|
||||
LOGGER.create_future_from_class($p)
|
||||
}};
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ async fn main(spawner: Spawner) {
|
||||
let c_sda = p.PIN_1;
|
||||
let c_scl = p.PIN_0;
|
||||
let mut config = i2c::Config::default();
|
||||
config.frequency = 5_000;
|
||||
config.frequency = 1_000_000;
|
||||
let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
|
||||
|
||||
unwrap!(spawner.spawn(controller_task(controller)));
|
||||
|
@ -12,7 +12,7 @@ use embassy_rp::pio::{
|
||||
Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
||||
};
|
||||
use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_time::Timer;
|
||||
use embassy_time::{Duration, Ticker, Timer};
|
||||
use fixed::types::U24F8;
|
||||
use fixed_macro::fixed;
|
||||
use smart_leds::RGB8;
|
||||
@ -107,6 +107,8 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
|
||||
|
||||
// DMA transfer
|
||||
self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
|
||||
|
||||
Timer::after_micros(55).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +145,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
|
||||
|
||||
// Loop forever making RGB values and pushing them out to the WS2812.
|
||||
let mut ticker = Ticker::every(Duration::from_millis(10));
|
||||
loop {
|
||||
for j in 0..(256 * 5) {
|
||||
debug!("New Colors:");
|
||||
@ -152,7 +155,7 @@ async fn main(_spawner: Spawner) {
|
||||
}
|
||||
ws2812.write(&data).await;
|
||||
|
||||
Timer::after_millis(10).await;
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
117
examples/rp/src/bin/usb_serial_with_logger.rs
Normal file
117
examples/rp/src/bin/usb_serial_with_logger.rs
Normal file
@ -0,0 +1,117 @@
|
||||
//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip as well as how to create multiple usb classes for one device
|
||||
//!
|
||||
//! This creates a USB serial port that echos. It will also print out logging information on a separate serial device
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::{info, panic};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::usb::{Driver, Instance, InterruptHandler};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::{Builder, Config};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Hello there!");
|
||||
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// Create the driver, from the HAL.
|
||||
let driver = Driver::new(p.USB, Irqs);
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("USB-serial example");
|
||||
config.serial_number = Some("12345678");
|
||||
config.max_power = 100;
|
||||
config.max_packet_size_0 = 64;
|
||||
|
||||
// Required for windows compatibility.
|
||||
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||
config.device_class = 0xEF;
|
||||
config.device_sub_class = 0x02;
|
||||
config.device_protocol = 0x01;
|
||||
config.composite_with_iads = true;
|
||||
|
||||
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||
// It needs some buffers for building the descriptors.
|
||||
let mut device_descriptor = [0; 256];
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 64];
|
||||
|
||||
let mut state = State::new();
|
||||
let mut logger_state = State::new();
|
||||
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
&mut device_descriptor,
|
||||
&mut config_descriptor,
|
||||
&mut bos_descriptor,
|
||||
&mut [], // no msos descriptors
|
||||
&mut control_buf,
|
||||
);
|
||||
|
||||
// Create classes on the builder.
|
||||
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
|
||||
|
||||
// Create a class for the logger
|
||||
let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64);
|
||||
|
||||
// Creates the logger and returns the logger future
|
||||
// Note: You'll need to use log::info! afterwards instead of info! for this to work (this also applies to all the other log::* macros)
|
||||
let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class);
|
||||
|
||||
// Build the builder.
|
||||
let mut usb = builder.build();
|
||||
|
||||
// Run the USB device.
|
||||
let usb_fut = usb.run();
|
||||
|
||||
// Do stuff with the class!
|
||||
let echo_fut = async {
|
||||
loop {
|
||||
class.wait_connection().await;
|
||||
log::info!("Connected");
|
||||
let _ = echo(&mut class).await;
|
||||
log::info!("Disconnected");
|
||||
}
|
||||
};
|
||||
|
||||
// Run everything concurrently.
|
||||
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
|
||||
join(usb_fut, join(echo_fut, log_fut)).await;
|
||||
}
|
||||
|
||||
struct Disconnected {}
|
||||
|
||||
impl From<EndpointError> for Disconnected {
|
||||
fn from(val: EndpointError) -> Self {
|
||||
match val {
|
||||
EndpointError::BufferOverflow => panic!("Buffer overflow"),
|
||||
EndpointError::Disabled => Disconnected {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
|
||||
let mut buf = [0; 64];
|
||||
loop {
|
||||
let n = class.read_packet(&mut buf).await?;
|
||||
let data = &buf[..n];
|
||||
info!("data: {:x}", data);
|
||||
class.write_packet(data).await?;
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing
|
||||
cortex-m-rt = "0.7.0"
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = "0.3"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
@ -3,15 +3,13 @@
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut config = Config::default();
|
||||
config.rcc.sys_ck = Some(Hertz(36_000_000));
|
||||
let config = Config::default();
|
||||
let _p = embassy_stm32::init(config);
|
||||
|
||||
loop {
|
||||
|
@ -21,9 +21,23 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.hse = Some(Hertz(8_000_000));
|
||||
config.rcc.sys_ck = Some(Hertz(48_000_000));
|
||||
config.rcc.pclk1 = Some(Hertz(24_000_000));
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
// Oscillator for bluepill, Bypass for nucleos.
|
||||
mode: HseMode::Oscillator,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
}
|
||||
let mut p = embassy_stm32::init(config);
|
||||
|
||||
info!("Hello World!");
|
||||
|
@ -3,16 +3,13 @@
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut config = Config::default();
|
||||
config.rcc.hse = Some(Hertz(8_000_000));
|
||||
config.rcc.sysclk = Some(Hertz(16_000_000));
|
||||
let config = Config::default();
|
||||
let _p = embassy_stm32::init(config);
|
||||
|
||||
loop {
|
||||
|
@ -21,11 +21,22 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.hse = Some(mhz(8));
|
||||
config.rcc.sysclk = Some(mhz(48));
|
||||
config.rcc.pclk1 = Some(mhz(24));
|
||||
config.rcc.pclk2 = Some(mhz(24));
|
||||
config.rcc.pll48 = true;
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: mhz(8),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
}
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
info!("Hello World!");
|
||||
|
@ -5,7 +5,6 @@ use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::{Adc, SampleTime};
|
||||
use embassy_stm32::peripherals::ADC1;
|
||||
use embassy_stm32::rcc::{AdcClockSource, Adcpres};
|
||||
use embassy_stm32::time::mhz;
|
||||
use embassy_stm32::{adc, bind_interrupts, Config};
|
||||
use embassy_time::{Delay, Timer};
|
||||
@ -18,12 +17,23 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut config = Config::default();
|
||||
config.rcc.sysclk = Some(mhz(64));
|
||||
config.rcc.hclk = Some(mhz(64));
|
||||
config.rcc.pclk1 = Some(mhz(32));
|
||||
config.rcc.pclk2 = Some(mhz(64));
|
||||
config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1));
|
||||
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: mhz(8),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);
|
||||
}
|
||||
let mut p = embassy_stm32::init(config);
|
||||
|
||||
info!("create adc...");
|
||||
|
@ -3,16 +3,13 @@
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut config = Config::default();
|
||||
config.rcc.hse = Some(Hertz(8_000_000));
|
||||
config.rcc.sysclk = Some(Hertz(16_000_000));
|
||||
let config = Config::default();
|
||||
let _p = embassy_stm32::init(config);
|
||||
|
||||
loop {
|
||||
|
@ -6,7 +6,6 @@ use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::{Adc, SampleTime};
|
||||
use embassy_stm32::opamp::{OpAmp, OpAmpGain};
|
||||
use embassy_stm32::peripherals::ADC2;
|
||||
use embassy_stm32::rcc::{AdcClockSource, Adcpres};
|
||||
use embassy_stm32::time::mhz;
|
||||
use embassy_stm32::{adc, bind_interrupts, Config};
|
||||
use embassy_time::{Delay, Timer};
|
||||
@ -19,12 +18,23 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut config = Config::default();
|
||||
config.rcc.sysclk = Some(mhz(64));
|
||||
config.rcc.hclk = Some(mhz(64));
|
||||
config.rcc.pclk1 = Some(mhz(32));
|
||||
config.rcc.pclk2 = Some(mhz(64));
|
||||
config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1));
|
||||
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: mhz(8),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);
|
||||
}
|
||||
let mut p = embassy_stm32::init(config);
|
||||
|
||||
info!("create adc...");
|
||||
|
@ -4,7 +4,6 @@
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::hrtim::*;
|
||||
use embassy_stm32::rcc::HrtimClockSource;
|
||||
use embassy_stm32::time::{khz, mhz};
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
@ -12,14 +11,26 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config: Config = Default::default();
|
||||
config.rcc.sysclk = Some(mhz(64));
|
||||
config.rcc.hclk = Some(mhz(64));
|
||||
config.rcc.pclk1 = Some(mhz(32));
|
||||
config.rcc.pclk2 = Some(mhz(64));
|
||||
let mut config = Config::default();
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: mhz(8),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.hrtim = HrtimClockSource::PllClk;
|
||||
|
||||
}
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
info!("Hello World!");
|
||||
|
||||
let ch1 = PwmPin::new_cha(p.PA8);
|
||||
|
@ -1,6 +1,6 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||
runner = "probe-rs run --chip STM32F767ZITx"
|
||||
runner = "probe-rs run --chip STM32F777ZITx"
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf"
|
||||
|
@ -5,8 +5,8 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
# Change stm32f767zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
||||
# Change stm32f777zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
@ -28,6 +28,8 @@ rand_core = "0.6.3"
|
||||
critical-section = "1.1"
|
||||
embedded-storage = "0.3.1"
|
||||
static_cell = "2"
|
||||
sha2 = { version = "0.10.8", default-features = false }
|
||||
hmac = "0.12.1"
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
||||
|
@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ETH => eth::InterruptHandler;
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
type Device = Ethernet<'static, ETH, GenericSMI>;
|
||||
|
78
examples/stm32f7/src/bin/hash.rs
Normal file
78
examples/stm32f7/src/bin/hash.rs
Normal file
@ -0,0 +1,78 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::hash::*;
|
||||
use embassy_stm32::{bind_interrupts, hash, peripherals, Config};
|
||||
use embassy_time::Instant;
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::{Digest, Sha256};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let config = Config::default();
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
|
||||
let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
|
||||
|
||||
let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs);
|
||||
|
||||
let hw_start_time = Instant::now();
|
||||
|
||||
// Compute a digest in hardware.
|
||||
let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);
|
||||
hw_hasher.update(&mut context, test_1).await;
|
||||
hw_hasher.update(&mut context, test_2).await;
|
||||
let mut hw_digest: [u8; 32] = [0; 32];
|
||||
hw_hasher.finish(context, &mut hw_digest).await;
|
||||
|
||||
let hw_end_time = Instant::now();
|
||||
let hw_execution_time = hw_end_time - hw_start_time;
|
||||
|
||||
let sw_start_time = Instant::now();
|
||||
|
||||
// Compute a digest in software.
|
||||
let mut sw_hasher = Sha256::new();
|
||||
sw_hasher.update(test_1);
|
||||
sw_hasher.update(test_2);
|
||||
let sw_digest = sw_hasher.finalize();
|
||||
|
||||
let sw_end_time = Instant::now();
|
||||
let sw_execution_time = sw_end_time - sw_start_time;
|
||||
|
||||
info!("Hardware Digest: {:?}", hw_digest);
|
||||
info!("Software Digest: {:?}", sw_digest[..]);
|
||||
info!("Hardware Execution Time: {:?}", hw_execution_time);
|
||||
info!("Software Execution Time: {:?}", sw_execution_time);
|
||||
assert_eq!(hw_digest, sw_digest[..]);
|
||||
|
||||
let hmac_key: [u8; 64] = [0x55; 64];
|
||||
|
||||
// Compute HMAC in hardware.
|
||||
let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));
|
||||
hw_hasher.update(&mut sha256hmac_context, test_1).await;
|
||||
hw_hasher.update(&mut sha256hmac_context, test_2).await;
|
||||
let mut hw_hmac: [u8; 32] = [0; 32];
|
||||
hw_hasher.finish(sha256hmac_context, &mut hw_hmac).await;
|
||||
|
||||
// Compute HMAC in software.
|
||||
let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();
|
||||
sw_mac.update(test_1);
|
||||
sw_mac.update(test_2);
|
||||
let sw_hmac = sw_mac.finalize().into_bytes();
|
||||
|
||||
info!("Hardware HMAC: {:?}", hw_hmac);
|
||||
info!("Software HMAC: {:?}", sw_hmac[..]);
|
||||
assert_eq!(hw_hmac, sw_hmac[..]);
|
||||
|
||||
loop {}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::{Adc, SampleTime};
|
||||
use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSource};
|
||||
use embassy_stm32::rcc::{AdcClockSource, Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk};
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -14,17 +14,17 @@ async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSI,
|
||||
prediv_m: PllM::DIV4,
|
||||
mul_n: PllN::MUL85,
|
||||
div_p: None,
|
||||
div_q: None,
|
||||
source: Pllsrc::HSI,
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL85,
|
||||
divp: None,
|
||||
divq: None,
|
||||
// Main system clock at 170 MHz
|
||||
div_r: Some(PllR::DIV2),
|
||||
divr: Some(PllRDiv::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.adc12_clock_source = AdcClockSource::SYS;
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
config.rcc.sys = Sysclk::PLL1_R;
|
||||
|
||||
let mut p = embassy_stm32::init(config);
|
||||
info!("Hello World!");
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSource};
|
||||
use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk};
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -13,16 +13,16 @@ async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSI,
|
||||
prediv_m: PllM::DIV4,
|
||||
mul_n: PllN::MUL85,
|
||||
div_p: None,
|
||||
div_q: None,
|
||||
source: Pllsrc::HSI,
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL85,
|
||||
divp: None,
|
||||
divq: None,
|
||||
// Main system clock at 170 MHz
|
||||
div_r: Some(PllR::DIV2),
|
||||
divr: Some(PllRDiv::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
config.rcc.sys = Sysclk::PLL1_R;
|
||||
|
||||
let _p = embassy_stm32::init(config);
|
||||
info!("Hello World!");
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSource};
|
||||
use embassy_stm32::rcc::{
|
||||
Clk48Src, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk,
|
||||
};
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::usb::{self, Driver, Instance};
|
||||
use embassy_stm32::{bind_interrupts, peripherals, Config};
|
||||
@ -24,25 +26,32 @@ async fn main(_spawner: Spawner) {
|
||||
// Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
|
||||
const USE_HSI48: bool = true;
|
||||
|
||||
let plldivq = if USE_HSI48 { None } else { Some(PllQ::DIV6) };
|
||||
let plldivq = if USE_HSI48 { None } else { Some(PllQDiv::DIV6) };
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSE(Hertz(8_000_000)),
|
||||
prediv_m: PllM::DIV2,
|
||||
mul_n: PllN::MUL72,
|
||||
div_p: None,
|
||||
div_q: plldivq,
|
||||
// Main system clock at 144 MHz
|
||||
div_r: Some(PllR::DIV2),
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Oscillator,
|
||||
});
|
||||
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: Pllsrc::HSE,
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL72,
|
||||
divp: None,
|
||||
divq: plldivq,
|
||||
// Main system clock at 144 MHz
|
||||
divr: Some(PllRDiv::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.sys = Sysclk::PLL1_R;
|
||||
config.rcc.boost = true; // BOOST!
|
||||
|
||||
if USE_HSI48 {
|
||||
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
|
||||
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true }));
|
||||
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });
|
||||
config.rcc.clk48_src = Clk48Src::HSI48;
|
||||
} else {
|
||||
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
|
||||
config.rcc.clk48_src = Clk48Src::PLL1_Q;
|
||||
}
|
||||
|
||||
let p = embassy_stm32::init(config);
|
||||
|
@ -14,9 +14,9 @@ rustflags = [
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
#target = "thumbv6m-none-eabi"
|
||||
#target = "thumbv7m-none-eabi"
|
||||
#target = "thumbv7em-none-eabi"
|
||||
target = "thumbv7em-none-eabi"
|
||||
#target = "thumbv8m.main-none-eabihf"
|
||||
|
||||
[env]
|
||||
|
@ -15,22 +15,25 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma"
|
||||
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
|
||||
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
|
||||
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"]
|
||||
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
|
||||
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"]
|
||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"]
|
||||
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"]
|
||||
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash"]
|
||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash"]
|
||||
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
|
||||
stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
|
||||
stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
|
||||
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
|
||||
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"]
|
||||
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"]
|
||||
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
|
||||
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"]
|
||||
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"]
|
||||
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
|
||||
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"]
|
||||
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
|
||||
stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
|
||||
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"]
|
||||
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
|
||||
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
||||
stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
|
||||
stm32h503rb = ["embassy-stm32/stm32h503rb", "rng"]
|
||||
|
||||
hash = []
|
||||
eth = ["embassy-executor/task-arena-size-16384"]
|
||||
rng = []
|
||||
sdmmc = []
|
||||
@ -74,6 +77,8 @@ static_cell = "2"
|
||||
portable-atomic = { version = "1.5", features = [] }
|
||||
|
||||
chrono = { version = "^0.4", default-features = false, optional = true}
|
||||
sha2 = { version = "0.10.8", default-features = false }
|
||||
hmac = "0.12.1"
|
||||
|
||||
# BEGIN TESTS
|
||||
# Generated by gen_test.py. DO NOT EDIT.
|
||||
@ -107,6 +112,11 @@ name = "gpio"
|
||||
path = "src/bin/gpio.rs"
|
||||
required-features = []
|
||||
|
||||
[[bin]]
|
||||
name = "hash"
|
||||
path = "src/bin/hash.rs"
|
||||
required-features = [ "hash",]
|
||||
|
||||
[[bin]]
|
||||
name = "rng"
|
||||
path = "src/bin/rng.rs"
|
||||
|
@ -16,6 +16,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
feature = "stm32l073rz",
|
||||
// wrong ram size in stm32-data
|
||||
feature = "stm32wl55jc",
|
||||
// no VTOR, so interrupts can't work when running from RAM
|
||||
feature = "stm32f091rc",
|
||||
)) {
|
||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||
println!("cargo:rerun-if-changed=link.x");
|
||||
|
101
tests/stm32/src/bin/hash.rs
Normal file
101
tests/stm32/src/bin/hash.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// required-features: hash
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[path = "../common.rs"]
|
||||
mod common;
|
||||
use common::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::hash::*;
|
||||
use embassy_stm32::{bind_interrupts, hash, peripherals};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::{Digest, Sha224, Sha256};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))]
|
||||
bind_interrupts!(struct Irqs {
|
||||
HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
|
||||
});
|
||||
|
||||
#[cfg(any(
|
||||
feature = "stm32wba52cg",
|
||||
feature = "stm32l552ze",
|
||||
feature = "stm32h563zi",
|
||||
feature = "stm32h503rb",
|
||||
feature = "stm32u5a5zj",
|
||||
feature = "stm32u585ai"
|
||||
))]
|
||||
bind_interrupts!(struct Irqs {
|
||||
HASH => hash::InterruptHandler<peripherals::HASH>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
|
||||
let mut hw_hasher = Hash::new(p.HASH, NoDma, Irqs);
|
||||
|
||||
let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
|
||||
let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
|
||||
let test_3: &[u8] = b"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk";
|
||||
|
||||
// Start an SHA-256 digest.
|
||||
let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);
|
||||
hw_hasher.update_blocking(&mut sha256context, test_1);
|
||||
|
||||
// Interrupt the SHA-256 digest to compute an SHA-224 digest.
|
||||
let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8, None);
|
||||
hw_hasher.update_blocking(&mut sha224context, test_3);
|
||||
let mut sha224_digest_buffer: [u8; 28] = [0; 28];
|
||||
let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer);
|
||||
|
||||
// Finish the SHA-256 digest.
|
||||
hw_hasher.update_blocking(&mut sha256context, test_2);
|
||||
let mut sha256_digest_buffer: [u8; 32] = [0; 32];
|
||||
let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer);
|
||||
|
||||
// Compute the SHA-256 digest in software.
|
||||
let mut sw_sha256_hasher = Sha256::new();
|
||||
sw_sha256_hasher.update(test_1);
|
||||
sw_sha256_hasher.update(test_2);
|
||||
let sw_sha256_digest = sw_sha256_hasher.finalize();
|
||||
|
||||
//Compute the SHA-224 digest in software.
|
||||
let mut sw_sha224_hasher = Sha224::new();
|
||||
sw_sha224_hasher.update(test_3);
|
||||
let sw_sha224_digest = sw_sha224_hasher.finalize();
|
||||
|
||||
// Compare the SHA-256 digests.
|
||||
info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer);
|
||||
info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]);
|
||||
defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]);
|
||||
|
||||
// Compare the SHA-224 digests.
|
||||
info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer);
|
||||
info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]);
|
||||
defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]);
|
||||
|
||||
let hmac_key: [u8; 64] = [0x55; 64];
|
||||
|
||||
// Compute HMAC in hardware.
|
||||
let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));
|
||||
hw_hasher.update_blocking(&mut sha256hmac_context, test_1);
|
||||
hw_hasher.update_blocking(&mut sha256hmac_context, test_2);
|
||||
let mut hw_hmac: [u8; 32] = [0; 32];
|
||||
hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac);
|
||||
|
||||
// Compute HMAC in software.
|
||||
let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();
|
||||
sw_mac.update(test_1);
|
||||
sw_mac.update(test_2);
|
||||
let sw_hmac = sw_mac.finalize().into_bytes();
|
||||
|
||||
info!("Hardware HMAC: {:?}", hw_hmac);
|
||||
info!("Software HMAC: {:?}", sw_hmac[..]);
|
||||
defmt::assert!(hw_hmac == sw_hmac[..]);
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
@ -54,6 +54,10 @@ teleprobe_meta::target!(b"nucleo-stm32l496zg");
|
||||
teleprobe_meta::target!(b"nucleo-stm32wl55jc");
|
||||
#[cfg(feature = "stm32wba52cg")]
|
||||
teleprobe_meta::target!(b"nucleo-stm32wba52cg");
|
||||
#[cfg(feature = "stm32f091rc")]
|
||||
teleprobe_meta::target!(b"nucleo-stm32f091rc");
|
||||
#[cfg(feature = "stm32h503rb")]
|
||||
teleprobe_meta::target!(b"nucleo-stm32h503rb");
|
||||
|
||||
macro_rules! define_peris {
|
||||
($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {
|
||||
@ -85,6 +89,12 @@ macro_rules! define_peris {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32f091rc")]
|
||||
define_peris!(
|
||||
UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
|
||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||
);
|
||||
#[cfg(feature = "stm32f103c8")]
|
||||
define_peris!(
|
||||
UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
|
||||
@ -157,6 +167,12 @@ define_peris!(
|
||||
SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,
|
||||
@irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;},
|
||||
);
|
||||
#[cfg(feature = "stm32h503rb")]
|
||||
define_peris!(
|
||||
UART = USART1, UART_TX = PB14, UART_RX = PB15, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,
|
||||
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,
|
||||
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
|
||||
);
|
||||
#[cfg(feature = "stm32c031c6")]
|
||||
define_peris!(
|
||||
UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
|
||||
@ -247,6 +263,39 @@ pub fn config() -> Config {
|
||||
config.rcc = embassy_stm32::rcc::WPAN_DEFAULT;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32f091rc")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL6,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV1;
|
||||
}
|
||||
#[cfg(feature = "stm32f103c8")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Oscillator,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
}
|
||||
#[cfg(feature = "stm32f207zg")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
@ -276,6 +325,24 @@ pub fn config() -> Config {
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV2;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32f303ze")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
config.rcc.pll = Some(Pll {
|
||||
src: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL9,
|
||||
});
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV2;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32f429zi")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
@ -364,6 +431,31 @@ pub fn config() -> Config {
|
||||
config.rcc.voltage_scale = VoltageScale::Scale0;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32h503rb")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hsi = None;
|
||||
config.rcc.hsi48 = Some(Default::default()); // needed for RNG
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(24_000_000),
|
||||
mode: HseMode::Oscillator,
|
||||
});
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV6,
|
||||
mul: PllMul::MUL125,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV2),
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb3_pre = APBPrescaler::DIV1;
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
config.rcc.voltage_scale = VoltageScale::Scale0;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
|
Loading…
Reference in New Issue
Block a user