Merge remote-tracking branch 'origin/main' into u5_adc
This commit is contained in:
		
						commit
						60347976b2
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -5,3 +5,4 @@ Cargo.lock
 | 
				
			|||||||
third_party
 | 
					third_party
 | 
				
			||||||
/Cargo.toml
 | 
					/Cargo.toml
 | 
				
			||||||
out/
 | 
					out/
 | 
				
			||||||
 | 
					.zed
 | 
				
			||||||
 | 
				
			|||||||
@ -88,8 +88,7 @@ Then, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[source, bash]
 | 
					[source, bash]
 | 
				
			||||||
----
 | 
					----
 | 
				
			||||||
shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt
 | 
					shasum -a 512 -b $FIRMWARE_DIR/myfirmware | head -c128 | xxd -p -r > $SECRETS_DIR/message.txt
 | 
				
			||||||
cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt
 | 
					 | 
				
			||||||
signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig
 | 
					signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig
 | 
				
			||||||
cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed
 | 
					cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed
 | 
				
			||||||
tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed
 | 
					tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
 | 
				
			|||||||
use embassy_sync::blocking_mutex::Mutex;
 | 
					use embassy_sync::blocking_mutex::Mutex;
 | 
				
			||||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
 | 
					use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
 | 
					use crate::{State, DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Errors returned by bootloader
 | 
					/// Errors returned by bootloader
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug)]
 | 
					#[derive(PartialEq, Eq, Debug)]
 | 
				
			||||||
@ -276,7 +276,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
 | 
				
			|||||||
                self.state.erase(0, self.state.capacity() as u32)?;
 | 
					                self.state.erase(0, self.state.capacity() as u32)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Set magic
 | 
					                // Set magic
 | 
				
			||||||
                state_word.fill(BOOT_MAGIC);
 | 
					                state_word.fill(REVERT_MAGIC);
 | 
				
			||||||
                self.state.write(0, state_word)?;
 | 
					                self.state.write(0, state_word)?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -411,6 +411,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
 | 
				
			|||||||
            Ok(State::Swap)
 | 
					            Ok(State::Swap)
 | 
				
			||||||
        } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {
 | 
					        } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {
 | 
				
			||||||
            Ok(State::DfuDetach)
 | 
					            Ok(State::DfuDetach)
 | 
				
			||||||
 | 
					        } else if !state_word.iter().any(|&b| b != REVERT_MAGIC) {
 | 
				
			||||||
 | 
					            Ok(State::Revert)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Ok(State::Boot)
 | 
					            Ok(State::Boot)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -289,7 +289,8 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Make sure we are running a booted firmware to avoid reverting to a bad state.
 | 
					    // Make sure we are running a booted firmware to avoid reverting to a bad state.
 | 
				
			||||||
    async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
 | 
					    async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        if self.get_state().await? == State::Boot {
 | 
					        let state = self.get_state().await?;
 | 
				
			||||||
 | 
					        if state == State::Boot || state == State::DfuDetach || state == State::Revert {
 | 
				
			||||||
            Ok(())
 | 
					            Ok(())
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Err(FirmwareUpdaterError::BadState)
 | 
					            Err(FirmwareUpdaterError::BadState)
 | 
				
			||||||
@ -303,12 +304,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
 | 
				
			|||||||
    /// `mark_booted`.
 | 
					    /// `mark_booted`.
 | 
				
			||||||
    pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
 | 
					    pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
 | 
				
			||||||
        self.state.read(0, &mut self.aligned).await?;
 | 
					        self.state.read(0, &mut self.aligned).await?;
 | 
				
			||||||
 | 
					        Ok(State::from(&self.aligned))
 | 
				
			||||||
        if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
					 | 
				
			||||||
            Ok(State::Swap)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            Ok(State::Boot)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark to trigger firmware swap on next boot.
 | 
					    /// Mark to trigger firmware swap on next boot.
 | 
				
			||||||
 | 
				
			|||||||
@ -324,7 +324,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Make sure we are running a booted firmware to avoid reverting to a bad state.
 | 
					    // Make sure we are running a booted firmware to avoid reverting to a bad state.
 | 
				
			||||||
    fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
 | 
					    fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
 | 
				
			||||||
        if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach {
 | 
					        let state = self.get_state()?;
 | 
				
			||||||
 | 
					        if state == State::Boot || state == State::DfuDetach || state == State::Revert {
 | 
				
			||||||
            Ok(())
 | 
					            Ok(())
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Err(FirmwareUpdaterError::BadState)
 | 
					            Err(FirmwareUpdaterError::BadState)
 | 
				
			||||||
@ -338,14 +339,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
 | 
				
			|||||||
    /// `mark_booted`.
 | 
					    /// `mark_booted`.
 | 
				
			||||||
    pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
 | 
					    pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
 | 
				
			||||||
        self.state.read(0, &mut self.aligned)?;
 | 
					        self.state.read(0, &mut self.aligned)?;
 | 
				
			||||||
 | 
					        Ok(State::from(&self.aligned))
 | 
				
			||||||
        if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
 | 
					 | 
				
			||||||
            Ok(State::Swap)
 | 
					 | 
				
			||||||
        } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) {
 | 
					 | 
				
			||||||
            Ok(State::DfuDetach)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            Ok(State::Boot)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mark to trigger firmware swap on next boot.
 | 
					    /// Mark to trigger firmware swap on next boot.
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ pub use firmware_updater::{
 | 
				
			|||||||
    FirmwareUpdaterError,
 | 
					    FirmwareUpdaterError,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub(crate) const REVERT_MAGIC: u8 = 0xC0;
 | 
				
			||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
 | 
					pub(crate) const BOOT_MAGIC: u8 = 0xD0;
 | 
				
			||||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
 | 
					pub(crate) const SWAP_MAGIC: u8 = 0xF0;
 | 
				
			||||||
pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
 | 
					pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
 | 
				
			||||||
@ -37,10 +38,30 @@ pub enum State {
 | 
				
			|||||||
    Boot,
 | 
					    Boot,
 | 
				
			||||||
    /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
 | 
					    /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
 | 
				
			||||||
    Swap,
 | 
					    Swap,
 | 
				
			||||||
 | 
					    /// Bootloader has reverted the active partition with the dfu partition and will attempt boot.
 | 
				
			||||||
 | 
					    Revert,
 | 
				
			||||||
    /// Application has received a request to reboot into DFU mode to apply an update.
 | 
					    /// Application has received a request to reboot into DFU mode to apply an update.
 | 
				
			||||||
    DfuDetach,
 | 
					    DfuDetach,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> From<T> for State
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    T: AsRef<[u8]>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn from(magic: T) -> State {
 | 
				
			||||||
 | 
					        let magic = magic.as_ref();
 | 
				
			||||||
 | 
					        if !magic.iter().any(|&b| b != SWAP_MAGIC) {
 | 
				
			||||||
 | 
					            State::Swap
 | 
				
			||||||
 | 
					        } else if !magic.iter().any(|&b| b != REVERT_MAGIC) {
 | 
				
			||||||
 | 
					            State::Revert
 | 
				
			||||||
 | 
					        } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) {
 | 
				
			||||||
 | 
					            State::DfuDetach
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            State::Boot
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
 | 
					/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
 | 
				
			||||||
#[repr(align(32))]
 | 
					#[repr(align(32))]
 | 
				
			||||||
pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
 | 
					pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
 | 
				
			||||||
@ -157,6 +178,9 @@ mod tests {
 | 
				
			|||||||
        // Running again should cause a revert
 | 
					        // Running again should cause a revert
 | 
				
			||||||
        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
 | 
					        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Next time we know it was reverted
 | 
				
			||||||
 | 
					        assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut read_buf = [0; FIRMWARE_SIZE];
 | 
					        let mut read_buf = [0; FIRMWARE_SIZE];
 | 
				
			||||||
        flash.active().read(0, &mut read_buf).unwrap();
 | 
					        flash.active().read(0, &mut read_buf).unwrap();
 | 
				
			||||||
        assert_eq!(ORIGINAL, read_buf);
 | 
					        assert_eq!(ORIGINAL, read_buf);
 | 
				
			||||||
 | 
				
			|||||||
@ -120,7 +120,7 @@ impl<'a> Control<'a> {
 | 
				
			|||||||
            pwd: unwrap!(String::try_from(password)),
 | 
					            pwd: unwrap!(String::try_from(password)),
 | 
				
			||||||
            bssid: String::new(),
 | 
					            bssid: String::new(),
 | 
				
			||||||
            listen_interval: 3,
 | 
					            listen_interval: 3,
 | 
				
			||||||
            is_wpa3_supported: false,
 | 
					            is_wpa3_supported: true,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
 | 
					        ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
 | 
				
			||||||
        self.state_ch.set_link_state(LinkState::Up);
 | 
					        self.state_ch.set_link_state(LinkState::Up);
 | 
				
			||||||
 | 
				
			|||||||
@ -137,7 +137,7 @@ where
 | 
				
			|||||||
    let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
 | 
					    let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
 | 
				
			||||||
    let state_ch = ch_runner.state_runner();
 | 
					    let state_ch = ch_runner.state_runner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut runner = Runner {
 | 
					    let runner = Runner {
 | 
				
			||||||
        ch: ch_runner,
 | 
					        ch: ch_runner,
 | 
				
			||||||
        state_ch,
 | 
					        state_ch,
 | 
				
			||||||
        shared: &state.shared,
 | 
					        shared: &state.shared,
 | 
				
			||||||
@ -148,7 +148,6 @@ where
 | 
				
			|||||||
        spi,
 | 
					        spi,
 | 
				
			||||||
        heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
 | 
					        heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    runner.init().await;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (device, Control::new(state_ch, &state.shared), runner)
 | 
					    (device, Control::new(state_ch, &state.shared), runner)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -174,8 +173,6 @@ where
 | 
				
			|||||||
    IN: InputPin + Wait,
 | 
					    IN: InputPin + Wait,
 | 
				
			||||||
    OUT: OutputPin,
 | 
					    OUT: OutputPin,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    async fn init(&mut self) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Run the packet processing.
 | 
					    /// Run the packet processing.
 | 
				
			||||||
    pub async fn run(mut self) -> ! {
 | 
					    pub async fn run(mut self) -> ! {
 | 
				
			||||||
        debug!("resetting...");
 | 
					        debug!("resetting...");
 | 
				
			||||||
 | 
				
			|||||||
@ -260,7 +260,10 @@ pub struct Stack<'d> {
 | 
				
			|||||||
pub(crate) struct Inner {
 | 
					pub(crate) struct Inner {
 | 
				
			||||||
    pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased.
 | 
					    pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased.
 | 
				
			||||||
    pub(crate) iface: Interface,
 | 
					    pub(crate) iface: Interface,
 | 
				
			||||||
 | 
					    /// Waker used for triggering polls.
 | 
				
			||||||
    pub(crate) waker: WakerRegistration,
 | 
					    pub(crate) waker: WakerRegistration,
 | 
				
			||||||
 | 
					    /// Waker used for waiting for link up or config up.
 | 
				
			||||||
 | 
					    state_waker: WakerRegistration,
 | 
				
			||||||
    hardware_address: HardwareAddress,
 | 
					    hardware_address: HardwareAddress,
 | 
				
			||||||
    next_local_port: u16,
 | 
					    next_local_port: u16,
 | 
				
			||||||
    link_up: bool,
 | 
					    link_up: bool,
 | 
				
			||||||
@ -270,7 +273,6 @@ pub(crate) struct Inner {
 | 
				
			|||||||
    static_v6: Option<StaticConfigV6>,
 | 
					    static_v6: Option<StaticConfigV6>,
 | 
				
			||||||
    #[cfg(feature = "dhcpv4")]
 | 
					    #[cfg(feature = "dhcpv4")]
 | 
				
			||||||
    dhcp_socket: Option<SocketHandle>,
 | 
					    dhcp_socket: Option<SocketHandle>,
 | 
				
			||||||
    config_waker: WakerRegistration,
 | 
					 | 
				
			||||||
    #[cfg(feature = "dns")]
 | 
					    #[cfg(feature = "dns")]
 | 
				
			||||||
    dns_socket: SocketHandle,
 | 
					    dns_socket: SocketHandle,
 | 
				
			||||||
    #[cfg(feature = "dns")]
 | 
					    #[cfg(feature = "dns")]
 | 
				
			||||||
@ -326,6 +328,7 @@ pub fn new<'d, D: Driver, const SOCK: usize>(
 | 
				
			|||||||
        sockets,
 | 
					        sockets,
 | 
				
			||||||
        iface,
 | 
					        iface,
 | 
				
			||||||
        waker: WakerRegistration::new(),
 | 
					        waker: WakerRegistration::new(),
 | 
				
			||||||
 | 
					        state_waker: WakerRegistration::new(),
 | 
				
			||||||
        next_local_port,
 | 
					        next_local_port,
 | 
				
			||||||
        hardware_address,
 | 
					        hardware_address,
 | 
				
			||||||
        link_up: false,
 | 
					        link_up: false,
 | 
				
			||||||
@ -335,7 +338,6 @@ pub fn new<'d, D: Driver, const SOCK: usize>(
 | 
				
			|||||||
        static_v6: None,
 | 
					        static_v6: None,
 | 
				
			||||||
        #[cfg(feature = "dhcpv4")]
 | 
					        #[cfg(feature = "dhcpv4")]
 | 
				
			||||||
        dhcp_socket: None,
 | 
					        dhcp_socket: None,
 | 
				
			||||||
        config_waker: WakerRegistration::new(),
 | 
					 | 
				
			||||||
        #[cfg(feature = "dns")]
 | 
					        #[cfg(feature = "dns")]
 | 
				
			||||||
        dns_socket,
 | 
					        dns_socket,
 | 
				
			||||||
        #[cfg(feature = "dns")]
 | 
					        #[cfg(feature = "dns")]
 | 
				
			||||||
@ -421,10 +423,20 @@ impl<'d> Stack<'d> {
 | 
				
			|||||||
        v4_up || v6_up
 | 
					        v4_up || v6_up
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Wait for the network device to obtain a link signal.
 | 
				
			||||||
 | 
					    pub async fn wait_link_up(&self) {
 | 
				
			||||||
 | 
					        self.wait(|| self.is_link_up()).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Wait for the network device to lose link signal.
 | 
				
			||||||
 | 
					    pub async fn wait_link_down(&self) {
 | 
				
			||||||
 | 
					        self.wait(|| !self.is_link_up()).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Wait for the network stack to obtain a valid IP configuration.
 | 
					    /// Wait for the network stack to obtain a valid IP configuration.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// ## Notes:
 | 
					    /// ## Notes:
 | 
				
			||||||
    /// - Ensure [`Stack::run`] has been called before using this function.
 | 
					    /// - Ensure [`Runner::run`] has been started before using this function.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// - This function may never return (e.g. if no configuration is obtained through DHCP).
 | 
					    /// - This function may never return (e.g. if no configuration is obtained through DHCP).
 | 
				
			||||||
    /// The caller is supposed to handle a timeout for this case.
 | 
					    /// The caller is supposed to handle a timeout for this case.
 | 
				
			||||||
@ -451,13 +463,17 @@ impl<'d> Stack<'d> {
 | 
				
			|||||||
    /// // ...
 | 
					    /// // ...
 | 
				
			||||||
    /// ```
 | 
					    /// ```
 | 
				
			||||||
    pub async fn wait_config_up(&self) {
 | 
					    pub async fn wait_config_up(&self) {
 | 
				
			||||||
        // If the config is up already, we can return immediately.
 | 
					        self.wait(|| self.is_config_up()).await
 | 
				
			||||||
        if self.is_config_up() {
 | 
					    }
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        poll_fn(|cx| {
 | 
					    /// Wait for the network stack to lose a valid IP configuration.
 | 
				
			||||||
            if self.is_config_up() {
 | 
					    pub async fn wait_config_down(&self) {
 | 
				
			||||||
 | 
					        self.wait(|| !self.is_config_up()).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future<Output = ()> + 'a {
 | 
				
			||||||
 | 
					        poll_fn(move |cx| {
 | 
				
			||||||
 | 
					            if predicate() {
 | 
				
			||||||
                Poll::Ready(())
 | 
					                Poll::Ready(())
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // If the config is not up, we register a waker that is woken up
 | 
					                // If the config is not up, we register a waker that is woken up
 | 
				
			||||||
@ -465,13 +481,12 @@ impl<'d> Stack<'d> {
 | 
				
			|||||||
                trace!("Waiting for config up");
 | 
					                trace!("Waiting for config up");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.with_mut(|i| {
 | 
					                self.with_mut(|i| {
 | 
				
			||||||
                    i.config_waker.register(cx.waker());
 | 
					                    i.state_waker.register(cx.waker());
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Poll::Pending
 | 
					                Poll::Pending
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get the current IPv4 configuration.
 | 
					    /// Get the current IPv4 configuration.
 | 
				
			||||||
@ -775,7 +790,7 @@ impl Inner {
 | 
				
			|||||||
                .update_servers(&dns_servers[..count]);
 | 
					                .update_servers(&dns_servers[..count]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.config_waker.wake();
 | 
					        self.state_waker.wake();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) {
 | 
					    fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) {
 | 
				
			||||||
@ -813,6 +828,7 @@ impl Inner {
 | 
				
			|||||||
        // Print when changed
 | 
					        // Print when changed
 | 
				
			||||||
        if old_link_up != self.link_up {
 | 
					        if old_link_up != self.link_up {
 | 
				
			||||||
            info!("link_up = {:?}", self.link_up);
 | 
					            info!("link_up = {:?}", self.link_up);
 | 
				
			||||||
 | 
					            self.state_waker.wake();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[allow(unused_mut)]
 | 
					        #[allow(unused_mut)]
 | 
				
			||||||
 | 
				
			|||||||
@ -847,6 +847,10 @@ impl<'d, T: GpinPin> Gpin<'d, T> {
 | 
				
			|||||||
        into_ref!(gpin);
 | 
					        into_ref!(gpin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08));
 | 
					        gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08));
 | 
				
			||||||
 | 
					        #[cfg(feature = "_rp235x")]
 | 
				
			||||||
 | 
					        gpin.pad_ctrl().write(|w| {
 | 
				
			||||||
 | 
					            w.set_iso(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Gpin {
 | 
					        Gpin {
 | 
				
			||||||
            gpin: gpin.map_into(),
 | 
					            gpin: gpin.map_into(),
 | 
				
			||||||
@ -861,6 +865,7 @@ impl<'d, T: GpinPin> Gpin<'d, T> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl<'d, T: GpinPin> Drop for Gpin<'d, T> {
 | 
					impl<'d, T: GpinPin> Drop for Gpin<'d, T> {
 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        self.gpin.pad_ctrl().write(|_| {});
 | 
				
			||||||
        self.gpin
 | 
					        self.gpin
 | 
				
			||||||
            .gpio()
 | 
					            .gpio()
 | 
				
			||||||
            .ctrl()
 | 
					            .ctrl()
 | 
				
			||||||
@ -921,11 +926,15 @@ pub struct Gpout<'d, T: GpoutPin> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: GpoutPin> Gpout<'d, T> {
 | 
					impl<'d, T: GpoutPin> Gpout<'d, T> {
 | 
				
			||||||
    /// Create new general purpose cloud output.
 | 
					    /// Create new general purpose clock output.
 | 
				
			||||||
    pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self {
 | 
					    pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self {
 | 
				
			||||||
        into_ref!(gpout);
 | 
					        into_ref!(gpout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08));
 | 
					        gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08));
 | 
				
			||||||
 | 
					        #[cfg(feature = "_rp235x")]
 | 
				
			||||||
 | 
					        gpout.pad_ctrl().write(|w| {
 | 
				
			||||||
 | 
					            w.set_iso(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self { gpout }
 | 
					        Self { gpout }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1005,6 +1014,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
 | 
				
			|||||||
impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
 | 
					impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        self.disable();
 | 
					        self.disable();
 | 
				
			||||||
 | 
					        self.gpout.pad_ctrl().write(|_| {});
 | 
				
			||||||
        self.gpout
 | 
					        self.gpout
 | 
				
			||||||
            .gpio()
 | 
					            .gpio()
 | 
				
			||||||
            .ctrl()
 | 
					            .ctrl()
 | 
				
			||||||
 | 
				
			|||||||
@ -16,9 +16,9 @@ use crate::{interrupt, pac, peripherals, Peripheral, RegExt};
 | 
				
			|||||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
 | 
					const NEW_AW: AtomicWaker = AtomicWaker::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(any(feature = "rp2040", feature = "rp235xa"))]
 | 
					#[cfg(any(feature = "rp2040", feature = "rp235xa"))]
 | 
				
			||||||
const BANK0_PIN_COUNT: usize = 30;
 | 
					pub(crate) const BANK0_PIN_COUNT: usize = 30;
 | 
				
			||||||
#[cfg(feature = "rp235xb")]
 | 
					#[cfg(feature = "rp235xb")]
 | 
				
			||||||
const BANK0_PIN_COUNT: usize = 48;
 | 
					pub(crate) const BANK0_PIN_COUNT: usize = 48;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT];
 | 
					static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT];
 | 
				
			||||||
#[cfg(feature = "qspi-as-gpio")]
 | 
					#[cfg(feature = "qspi-as-gpio")]
 | 
				
			||||||
@ -603,7 +603,7 @@ impl<'d> Flex<'d> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    fn bit(&self) -> u32 {
 | 
					    fn bit(&self) -> u32 {
 | 
				
			||||||
        1 << self.pin.pin()
 | 
					        1 << (self.pin.pin() % 32)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Set the pin's pull.
 | 
					    /// Set the pin's pull.
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,8 @@ pub mod rtc;
 | 
				
			|||||||
pub mod spi;
 | 
					pub mod spi;
 | 
				
			||||||
#[cfg(feature = "time-driver")]
 | 
					#[cfg(feature = "time-driver")]
 | 
				
			||||||
pub mod time_driver;
 | 
					pub mod time_driver;
 | 
				
			||||||
 | 
					#[cfg(feature = "_rp235x")]
 | 
				
			||||||
 | 
					pub mod trng;
 | 
				
			||||||
pub mod uart;
 | 
					pub mod uart;
 | 
				
			||||||
pub mod usb;
 | 
					pub mod usb;
 | 
				
			||||||
pub mod watchdog;
 | 
					pub mod watchdog;
 | 
				
			||||||
@ -402,6 +404,8 @@ embassy_hal_internal::peripherals! {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    WATCHDOG,
 | 
					    WATCHDOG,
 | 
				
			||||||
    BOOTSEL,
 | 
					    BOOTSEL,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRNG
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
 | 
					#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ use core::pin::Pin as FuturePin;
 | 
				
			|||||||
use core::sync::atomic::{compiler_fence, Ordering};
 | 
					use core::sync::atomic::{compiler_fence, Ordering};
 | 
				
			||||||
use core::task::{Context, Poll};
 | 
					use core::task::{Context, Poll};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use atomic_polyfill::{AtomicU32, AtomicU8};
 | 
					use atomic_polyfill::{AtomicU64, AtomicU8};
 | 
				
			||||||
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
 | 
					use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
 | 
				
			||||||
use embassy_sync::waitqueue::AtomicWaker;
 | 
					use embassy_sync::waitqueue::AtomicWaker;
 | 
				
			||||||
use fixed::types::extra::U8;
 | 
					use fixed::types::extra::U8;
 | 
				
			||||||
@ -731,6 +731,8 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
 | 
				
			|||||||
            w.set_autopull(config.shift_out.auto_fill);
 | 
					            w.set_autopull(config.shift_out.auto_fill);
 | 
				
			||||||
            w.set_autopush(config.shift_in.auto_fill);
 | 
					            w.set_autopush(config.shift_in.auto_fill);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "rp2040")]
 | 
				
			||||||
        sm.pinctrl().write(|w| {
 | 
					        sm.pinctrl().write(|w| {
 | 
				
			||||||
            w.set_sideset_count(config.pins.sideset_count);
 | 
					            w.set_sideset_count(config.pins.sideset_count);
 | 
				
			||||||
            w.set_set_count(config.pins.set_count);
 | 
					            w.set_set_count(config.pins.set_count);
 | 
				
			||||||
@ -740,6 +742,52 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
 | 
				
			|||||||
            w.set_set_base(config.pins.set_base);
 | 
					            w.set_set_base(config.pins.set_base);
 | 
				
			||||||
            w.set_out_base(config.pins.out_base);
 | 
					            w.set_out_base(config.pins.out_base);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "_rp235x")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let mut low_ok = true;
 | 
				
			||||||
 | 
					            let mut high_ok = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let in_pins = config.pins.in_base..config.pins.in_base + config.in_count;
 | 
				
			||||||
 | 
					            let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count;
 | 
				
			||||||
 | 
					            let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count;
 | 
				
			||||||
 | 
					            let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for pin_range in [in_pins, side_pins, set_pins, out_pins] {
 | 
				
			||||||
 | 
					                for pin in pin_range {
 | 
				
			||||||
 | 
					                    low_ok &= pin < 32;
 | 
				
			||||||
 | 
					                    high_ok &= pin >= 16;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if !low_ok && !high_ok {
 | 
				
			||||||
 | 
					                panic!(
 | 
				
			||||||
 | 
					                    "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}",
 | 
				
			||||||
 | 
					                    config.pins.in_base,
 | 
				
			||||||
 | 
					                    config.pins.in_base + config.in_count - 1,
 | 
				
			||||||
 | 
					                    config.pins.sideset_base,
 | 
				
			||||||
 | 
					                    config.pins.sideset_base + config.pins.sideset_count - 1,
 | 
				
			||||||
 | 
					                    config.pins.set_base,
 | 
				
			||||||
 | 
					                    config.pins.set_base + config.pins.set_count - 1,
 | 
				
			||||||
 | 
					                    config.pins.out_base,
 | 
				
			||||||
 | 
					                    config.pins.out_base + config.pins.out_count - 1,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let shift = if low_ok { 0 } else { 16 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            sm.pinctrl().write(|w| {
 | 
				
			||||||
 | 
					                w.set_sideset_count(config.pins.sideset_count);
 | 
				
			||||||
 | 
					                w.set_set_count(config.pins.set_count);
 | 
				
			||||||
 | 
					                w.set_out_count(config.pins.out_count);
 | 
				
			||||||
 | 
					                w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default());
 | 
				
			||||||
 | 
					                w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default());
 | 
				
			||||||
 | 
					                w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default());
 | 
				
			||||||
 | 
					                w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default());
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(origin) = config.origin {
 | 
					        if let Some(origin) = config.origin {
 | 
				
			||||||
            unsafe { instr::exec_jmp(self, origin) }
 | 
					            unsafe { instr::exec_jmp(self, origin) }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1006,6 +1054,10 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
 | 
				
			|||||||
    pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> {
 | 
					    pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> {
 | 
				
			||||||
        into_ref!(pin);
 | 
					        into_ref!(pin);
 | 
				
			||||||
        pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
 | 
					        pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
 | 
				
			||||||
 | 
					        #[cfg(feature = "_rp235x")]
 | 
				
			||||||
 | 
					        pin.pad_ctrl().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_iso(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        // we can be relaxed about this because we're &mut here and nothing is cached
 | 
					        // we can be relaxed about this because we're &mut here and nothing is cached
 | 
				
			||||||
        PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed);
 | 
					        PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed);
 | 
				
			||||||
        Pin {
 | 
					        Pin {
 | 
				
			||||||
@ -1187,7 +1239,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> {
 | 
				
			|||||||
// other way.
 | 
					// other way.
 | 
				
			||||||
pub struct State {
 | 
					pub struct State {
 | 
				
			||||||
    users: AtomicU8,
 | 
					    users: AtomicU8,
 | 
				
			||||||
    used_pins: AtomicU32,
 | 
					    used_pins: AtomicU64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn on_pio_drop<PIO: Instance>() {
 | 
					fn on_pio_drop<PIO: Instance>() {
 | 
				
			||||||
@ -1195,8 +1247,7 @@ fn on_pio_drop<PIO: Instance>() {
 | 
				
			|||||||
    if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
 | 
					    if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
 | 
				
			||||||
        let used_pins = state.used_pins.load(Ordering::Relaxed);
 | 
					        let used_pins = state.used_pins.load(Ordering::Relaxed);
 | 
				
			||||||
        let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
 | 
					        let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
 | 
				
			||||||
        // we only have 30 pins. don't test the other two since gpio() asserts.
 | 
					        for i in 0..crate::gpio::BANK0_PIN_COUNT {
 | 
				
			||||||
        for i in 0..30 {
 | 
					 | 
				
			||||||
            if used_pins & (1 << i) != 0 {
 | 
					            if used_pins & (1 << i) != 0 {
 | 
				
			||||||
                pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
 | 
					                pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -1221,7 +1272,7 @@ trait SealedInstance {
 | 
				
			|||||||
    fn state() -> &'static State {
 | 
					    fn state() -> &'static State {
 | 
				
			||||||
        static STATE: State = State {
 | 
					        static STATE: State = State {
 | 
				
			||||||
            users: AtomicU8::new(0),
 | 
					            users: AtomicU8::new(0),
 | 
				
			||||||
            used_pins: AtomicU32::new(0),
 | 
					            used_pins: AtomicU64::new(0),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &STATE
 | 
					        &STATE
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										405
									
								
								embassy-rp/src/trng.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										405
									
								
								embassy-rp/src/trng.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,405 @@
 | 
				
			|||||||
 | 
					//! True Random Number Generator (TRNG) driver.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use core::future::poll_fn;
 | 
				
			||||||
 | 
					use core::marker::PhantomData;
 | 
				
			||||||
 | 
					use core::ops::Not;
 | 
				
			||||||
 | 
					use core::task::Poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use embassy_hal_internal::Peripheral;
 | 
				
			||||||
 | 
					use embassy_sync::waitqueue::AtomicWaker;
 | 
				
			||||||
 | 
					use rand_core::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::interrupt::typelevel::{Binding, Interrupt};
 | 
				
			||||||
 | 
					use crate::peripherals::TRNG;
 | 
				
			||||||
 | 
					use crate::{interrupt, pac};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait SealedInstance {
 | 
				
			||||||
 | 
					    fn regs() -> pac::trng::Trng;
 | 
				
			||||||
 | 
					    fn waker() -> &'static AtomicWaker;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// TRNG peripheral instance.
 | 
				
			||||||
 | 
					#[allow(private_bounds)]
 | 
				
			||||||
 | 
					pub trait Instance: SealedInstance {
 | 
				
			||||||
 | 
					    /// Interrupt for this peripheral.
 | 
				
			||||||
 | 
					    type Interrupt: Interrupt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SealedInstance for TRNG {
 | 
				
			||||||
 | 
					    fn regs() -> rp_pac::trng::Trng {
 | 
				
			||||||
 | 
					        pac::TRNG
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn waker() -> &'static AtomicWaker {
 | 
				
			||||||
 | 
					        static WAKER: AtomicWaker = AtomicWaker::new();
 | 
				
			||||||
 | 
					        &WAKER
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Instance for TRNG {
 | 
				
			||||||
 | 
					    type Interrupt = interrupt::typelevel::TRNG_IRQ;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone, Debug)]
 | 
				
			||||||
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
 | 
					/// TRNG ROSC Inverter chain length options.
 | 
				
			||||||
 | 
					pub enum InverterChainLength {
 | 
				
			||||||
 | 
					    None = 0,
 | 
				
			||||||
 | 
					    One,
 | 
				
			||||||
 | 
					    Two,
 | 
				
			||||||
 | 
					    Three,
 | 
				
			||||||
 | 
					    Four,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<InverterChainLength> for u8 {
 | 
				
			||||||
 | 
					    fn from(value: InverterChainLength) -> Self {
 | 
				
			||||||
 | 
					        value as u8
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configuration for the TRNG.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// - Three built in entropy checks
 | 
				
			||||||
 | 
					/// - ROSC frequency controlled by selecting one of ROSC chain lengths
 | 
				
			||||||
 | 
					/// - Sample period in terms of system clock ticks
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Default configuration is based on the following from documentation:
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ----
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// RP2350 Datasheet 12.12.2
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ...
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// When configuring the TRNG block, consider the following principles:
 | 
				
			||||||
 | 
					/// • As average generation time increases, result quality increases and failed entropy checks decrease.
 | 
				
			||||||
 | 
					/// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and
 | 
				
			||||||
 | 
					/// failed entropy checks.
 | 
				
			||||||
 | 
					/// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or
 | 
				
			||||||
 | 
					/// 1 and sample count settings of 20-25.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ---
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly
 | 
				
			||||||
 | 
					/// by setting the sample period to 0. Random data collected this way is then passed through
 | 
				
			||||||
 | 
					/// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!).
 | 
				
			||||||
 | 
					#[non_exhaustive]
 | 
				
			||||||
 | 
					#[derive(Copy, Clone, Debug)]
 | 
				
			||||||
 | 
					pub struct Config {
 | 
				
			||||||
 | 
					    /// Bypass TRNG autocorrelation test
 | 
				
			||||||
 | 
					    pub disable_autocorrelation_test: bool,
 | 
				
			||||||
 | 
					    /// Bypass CRNGT test
 | 
				
			||||||
 | 
					    pub disable_crngt_test: bool,
 | 
				
			||||||
 | 
					    /// When set, the Von-Neuman balancer is bypassed (including the
 | 
				
			||||||
 | 
					    /// 32 consecutive bits test)
 | 
				
			||||||
 | 
					    pub disable_von_neumann_balancer: bool,
 | 
				
			||||||
 | 
					    /// Sets the number of rng_clk cycles between two consecutive
 | 
				
			||||||
 | 
					    /// ring oscillator samples.
 | 
				
			||||||
 | 
					    /// Note: If the von Neumann decorrelator is bypassed, the minimum value for
 | 
				
			||||||
 | 
					    /// sample counter must not be less than seventeen
 | 
				
			||||||
 | 
					    pub sample_count: u32,
 | 
				
			||||||
 | 
					    /// Selects the number of inverters (out of four possible
 | 
				
			||||||
 | 
					    /// selections) in the ring oscillator (the entropy source). Higher values select
 | 
				
			||||||
 | 
					    /// longer inverter chain lengths.
 | 
				
			||||||
 | 
					    pub inverter_chain_length: InverterChainLength,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Config {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Config {
 | 
				
			||||||
 | 
					            disable_autocorrelation_test: true,
 | 
				
			||||||
 | 
					            disable_crngt_test: true,
 | 
				
			||||||
 | 
					            disable_von_neumann_balancer: true,
 | 
				
			||||||
 | 
					            sample_count: 25,
 | 
				
			||||||
 | 
					            inverter_chain_length: InverterChainLength::One,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// True Random Number Generator Driver for RP2350
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// This driver provides async and blocking options.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// See [Config] for configuration details.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Usage example:
 | 
				
			||||||
 | 
					/// ```no_run
 | 
				
			||||||
 | 
					/// use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					/// use embassy_rp::trng::Trng;
 | 
				
			||||||
 | 
					/// use embassy_rp::peripherals::TRNG;
 | 
				
			||||||
 | 
					/// use embassy_rp::bind_interrupts;
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// bind_interrupts!(struct Irqs {
 | 
				
			||||||
 | 
					///     TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
 | 
				
			||||||
 | 
					/// });
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// #[embassy_executor::main]
 | 
				
			||||||
 | 
					/// async fn main(spawner: Spawner) {
 | 
				
			||||||
 | 
					///     let peripherals = embassy_rp::init(Default::default());
 | 
				
			||||||
 | 
					///     let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///     let mut randomness = [0u8; 58];
 | 
				
			||||||
 | 
					///     loop {
 | 
				
			||||||
 | 
					///         trng.fill_bytes(&mut randomness).await;
 | 
				
			||||||
 | 
					///         assert_ne!(randomness, [0u8; 58]);
 | 
				
			||||||
 | 
					///     }
 | 
				
			||||||
 | 
					///}
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					pub struct Trng<'d, T: Instance> {
 | 
				
			||||||
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// 12.12.1. Overview
 | 
				
			||||||
 | 
					/// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of
 | 
				
			||||||
 | 
					/// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC).
 | 
				
			||||||
 | 
					const TRNG_BLOCK_SIZE_BITS: usize = 192;
 | 
				
			||||||
 | 
					const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance> Trng<'d, T> {
 | 
				
			||||||
 | 
					    /// Create a new TRNG driver.
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        _trng: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd,
 | 
				
			||||||
 | 
					        config: Config,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let trng_config_register = regs.trng_config();
 | 
				
			||||||
 | 
					        trng_config_register.write(|w| {
 | 
				
			||||||
 | 
					            w.set_rnd_src_sel(config.inverter_chain_length.clone().into());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sample_count_register = regs.sample_cnt1();
 | 
				
			||||||
 | 
					        sample_count_register.write(|w| {
 | 
				
			||||||
 | 
					            *w = config.sample_count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let debug_control_register = regs.trng_debug_control();
 | 
				
			||||||
 | 
					        debug_control_register.write(|w| {
 | 
				
			||||||
 | 
					            w.set_auto_correlate_bypass(config.disable_autocorrelation_test);
 | 
				
			||||||
 | 
					            w.set_trng_crngt_bypass(config.disable_crngt_test);
 | 
				
			||||||
 | 
					            w.set_vnc_bypass(config.disable_von_neumann_balancer)
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Trng { phantom: PhantomData }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn start_rng(&self) {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        let source_enable_register = regs.rnd_source_enable();
 | 
				
			||||||
 | 
					        // Enable TRNG ROSC
 | 
				
			||||||
 | 
					        source_enable_register.write(|w| w.set_rnd_src_en(true));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop_rng(&self) {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        let source_enable_register = regs.rnd_source_enable();
 | 
				
			||||||
 | 
					        source_enable_register.write(|w| w.set_rnd_src_en(false));
 | 
				
			||||||
 | 
					        let reset_bits_counter_register = regs.rst_bits_counter();
 | 
				
			||||||
 | 
					        reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn enable_irq(&self) {
 | 
				
			||||||
 | 
					        unsafe { T::Interrupt::enable() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn disable_irq(&self) {
 | 
				
			||||||
 | 
					        T::Interrupt::disable();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn blocking_wait_for_successful_generation(&self) {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let trng_busy_register = regs.trng_busy();
 | 
				
			||||||
 | 
					        let trng_valid_register = regs.trng_valid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut success = false;
 | 
				
			||||||
 | 
					        while success.not() {
 | 
				
			||||||
 | 
					            while trng_busy_register.read().trng_busy() {}
 | 
				
			||||||
 | 
					            if trng_valid_register.read().ehr_valid().not() {
 | 
				
			||||||
 | 
					                if regs.rng_isr().read().autocorr_err() {
 | 
				
			||||||
 | 
					                    regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    panic!("RNG not busy, but ehr is not valid!")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                success = true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        let ehr_data_regs = [
 | 
				
			||||||
 | 
					            regs.ehr_data0(),
 | 
				
			||||||
 | 
					            regs.ehr_data1(),
 | 
				
			||||||
 | 
					            regs.ehr_data2(),
 | 
				
			||||||
 | 
					            regs.ehr_data3(),
 | 
				
			||||||
 | 
					            regs.ehr_data4(),
 | 
				
			||||||
 | 
					            regs.ehr_data5(),
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (i, reg) in ehr_data_regs.iter().enumerate() {
 | 
				
			||||||
 | 
					            buffer[i * 4..i * 4 + 4].copy_from_slice(®.read().to_ne_bytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {
 | 
				
			||||||
 | 
					        self.blocking_wait_for_successful_generation();
 | 
				
			||||||
 | 
					        self.read_ehr_registers_into_array(buffer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fill the buffer with random bytes, async version.
 | 
				
			||||||
 | 
					    pub async fn fill_bytes(&mut self, destination: &mut [u8]) {
 | 
				
			||||||
 | 
					        if destination.is_empty() {
 | 
				
			||||||
 | 
					            return; // Nothing to fill
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.start_rng();
 | 
				
			||||||
 | 
					        self.enable_irq();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut bytes_transferred = 0usize;
 | 
				
			||||||
 | 
					        let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let trng_busy_register = regs.trng_busy();
 | 
				
			||||||
 | 
					        let trng_valid_register = regs.trng_valid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let waker = T::waker();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let destination_length = destination.len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        poll_fn(|context| {
 | 
				
			||||||
 | 
					            waker.register(context.waker());
 | 
				
			||||||
 | 
					            if bytes_transferred == destination_length {
 | 
				
			||||||
 | 
					                self.stop_rng();
 | 
				
			||||||
 | 
					                self.disable_irq();
 | 
				
			||||||
 | 
					                Poll::Ready(())
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if trng_busy_register.read().trng_busy() {
 | 
				
			||||||
 | 
					                    Poll::Pending
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    if trng_valid_register.read().ehr_valid().not() {
 | 
				
			||||||
 | 
					                        panic!("RNG not busy, but ehr is not valid!")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    self.read_ehr_registers_into_array(&mut buffer);
 | 
				
			||||||
 | 
					                    let remaining = destination_length - bytes_transferred;
 | 
				
			||||||
 | 
					                    if remaining > TRNG_BLOCK_SIZE_BYTES {
 | 
				
			||||||
 | 
					                        destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES]
 | 
				
			||||||
 | 
					                            .copy_from_slice(&buffer);
 | 
				
			||||||
 | 
					                        bytes_transferred += TRNG_BLOCK_SIZE_BYTES
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        destination[bytes_transferred..bytes_transferred + remaining]
 | 
				
			||||||
 | 
					                            .copy_from_slice(&buffer[0..remaining]);
 | 
				
			||||||
 | 
					                        bytes_transferred += remaining
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if bytes_transferred == destination_length {
 | 
				
			||||||
 | 
					                        self.stop_rng();
 | 
				
			||||||
 | 
					                        self.disable_irq();
 | 
				
			||||||
 | 
					                        Poll::Ready(())
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        Poll::Pending
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fill the buffer with random bytes, blocking version.
 | 
				
			||||||
 | 
					    pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) {
 | 
				
			||||||
 | 
					        if destination.is_empty() {
 | 
				
			||||||
 | 
					            return; // Nothing to fill
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.start_rng();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) {
 | 
				
			||||||
 | 
					            self.blocking_wait_for_successful_generation();
 | 
				
			||||||
 | 
					            self.blocking_read_ehr_registers_into_array(&mut buffer);
 | 
				
			||||||
 | 
					            chunk.copy_from_slice(&buffer[..chunk.len()])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.stop_rng()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Return a random u32, blocking.
 | 
				
			||||||
 | 
					    pub fn blocking_next_u32(&mut self) -> u32 {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        self.start_rng();
 | 
				
			||||||
 | 
					        self.blocking_wait_for_successful_generation();
 | 
				
			||||||
 | 
					        // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to
 | 
				
			||||||
 | 
					        // clear all of the result registers.
 | 
				
			||||||
 | 
					        let result = regs.ehr_data5().read();
 | 
				
			||||||
 | 
					        self.stop_rng();
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Return a random u64, blocking.
 | 
				
			||||||
 | 
					    pub fn blocking_next_u64(&mut self) -> u64 {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        self.start_rng();
 | 
				
			||||||
 | 
					        self.blocking_wait_for_successful_generation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let low = regs.ehr_data4().read() as u64;
 | 
				
			||||||
 | 
					        // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to
 | 
				
			||||||
 | 
					        // clear all of the result registers.
 | 
				
			||||||
 | 
					        let result = (regs.ehr_data5().read() as u64) << 32 | low;
 | 
				
			||||||
 | 
					        self.stop_rng();
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> {
 | 
				
			||||||
 | 
					    fn next_u32(&mut self) -> u32 {
 | 
				
			||||||
 | 
					        self.blocking_next_u32()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next_u64(&mut self) -> u64 {
 | 
				
			||||||
 | 
					        self.blocking_next_u64()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn fill_bytes(&mut self, dest: &mut [u8]) {
 | 
				
			||||||
 | 
					        self.blocking_fill_bytes(dest)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        self.blocking_fill_bytes(dest);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/// TRNG interrupt handler.
 | 
				
			||||||
 | 
					pub struct InterruptHandler<T: Instance> {
 | 
				
			||||||
 | 
					    _trng: PhantomData<T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
 | 
				
			||||||
 | 
					    unsafe fn on_interrupt() {
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					        let isr = regs.rng_isr().read();
 | 
				
			||||||
 | 
					        // Clear ehr bit
 | 
				
			||||||
 | 
					        regs.rng_icr().write(|w| {
 | 
				
			||||||
 | 
					            w.set_ehr_valid(true);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if isr.ehr_valid() {
 | 
				
			||||||
 | 
					            T::waker().wake();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // 12.12.5. List of Registers
 | 
				
			||||||
 | 
					            // ...
 | 
				
			||||||
 | 
					            // TRNG: RNG_ISR Register
 | 
				
			||||||
 | 
					            // ...
 | 
				
			||||||
 | 
					            // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row.
 | 
				
			||||||
 | 
					            // When set, RNG ceases functioning until next reset
 | 
				
			||||||
 | 
					            if isr.autocorr_err() {
 | 
				
			||||||
 | 
					                warn!("TRNG Autocorrect error! Resetting TRNG");
 | 
				
			||||||
 | 
					                regs.trng_sw_reset().write(|w| {
 | 
				
			||||||
 | 
					                    w.set_trng_sw_reset(true);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -47,7 +47,7 @@ embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
 | 
				
			|||||||
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
 | 
					embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
 | 
				
			||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 | 
					embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 | 
				
			||||||
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
 | 
					embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
 | 
				
			||||||
embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" }
 | 
					embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false }
 | 
				
			||||||
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
 | 
					embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
 | 
				
			||||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
 | 
					embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
 | 
				
			||||||
embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" }
 | 
					embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" }
 | 
				
			||||||
@ -72,7 +72,7 @@ rand_core = "0.6.3"
 | 
				
			|||||||
sdio-host = "0.5.0"
 | 
					sdio-host = "0.5.0"
 | 
				
			||||||
critical-section = "1.1"
 | 
					critical-section = "1.1"
 | 
				
			||||||
#stm32-metapac = { version = "15" }
 | 
					#stm32-metapac = { version = "15" }
 | 
				
			||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" }
 | 
					stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
vcell = "0.1.3"
 | 
					vcell = "0.1.3"
 | 
				
			||||||
nb = "1.0.0"
 | 
					nb = "1.0.0"
 | 
				
			||||||
@ -99,7 +99,7 @@ proc-macro2 = "1.0.36"
 | 
				
			|||||||
quote = "1.0.15"
 | 
					quote = "1.0.15"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
 | 
					#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
 | 
				
			||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] }
 | 
					stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f", default-features = false, features = ["metadata"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[features]
 | 
					[features]
 | 
				
			||||||
default = ["rt"]
 | 
					default = ["rt"]
 | 
				
			||||||
@ -129,7 +129,7 @@ unstable-pac = []
 | 
				
			|||||||
#! ## Time
 | 
					#! ## Time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Enables additional driver features that depend on embassy-time
 | 
					## Enables additional driver features that depend on embassy-time
 | 
				
			||||||
time = ["dep:embassy-time"]
 | 
					time = ["dep:embassy-time", "embassy-embedded-hal/time"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Features starting with `_` are for internal use only. They're not intended
 | 
					# Features starting with `_` are for internal use only. They're not intended
 | 
				
			||||||
# to be enabled by other crates, and are not covered by semver guarantees.
 | 
					# to be enabled by other crates, and are not covered by semver guarantees.
 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ fn main() {
 | 
				
			|||||||
    let mut singletons: Vec<String> = Vec::new();
 | 
					    let mut singletons: Vec<String> = Vec::new();
 | 
				
			||||||
    for p in METADATA.peripherals {
 | 
					    for p in METADATA.peripherals {
 | 
				
			||||||
        if let Some(r) = &p.registers {
 | 
					        if let Some(r) = &p.registers {
 | 
				
			||||||
            if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" {
 | 
					            if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" {
 | 
				
			||||||
                // TODO: should we emit this for all peripherals? if so, we will need a list of all
 | 
					                // TODO: should we emit this for all peripherals? if so, we will need a list of all
 | 
				
			||||||
                // possible peripherals across all chips, so that we can declare the configs
 | 
					                // possible peripherals across all chips, so that we can declare the configs
 | 
				
			||||||
                // (replacing the hard-coded list of `peri_*` cfgs below)
 | 
					                // (replacing the hard-coded list of `peri_*` cfgs below)
 | 
				
			||||||
@ -111,6 +111,8 @@ fn main() {
 | 
				
			|||||||
        "peri_sai4",
 | 
					        "peri_sai4",
 | 
				
			||||||
        "peri_ucpd1",
 | 
					        "peri_ucpd1",
 | 
				
			||||||
        "peri_ucpd2",
 | 
					        "peri_ucpd2",
 | 
				
			||||||
 | 
					        "peri_usb_otg_fs",
 | 
				
			||||||
 | 
					        "peri_usb_otg_hs",
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
    cfgs.declare_all(&["mco", "mco1", "mco2"]);
 | 
					    cfgs.declare_all(&["mco", "mco1", "mco2"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -200,7 +200,7 @@ impl Registers {
 | 
				
			|||||||
            if header_reg.rtr().bit() {
 | 
					            if header_reg.rtr().bit() {
 | 
				
			||||||
                F::new_remote(id, len as usize)
 | 
					                F::new_remote(id, len as usize)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                F::new(id, &data)
 | 
					                F::new(id, &data[0..(len as usize)])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Abort request failed because the frame was already sent (or being sent) on
 | 
					            // Abort request failed because the frame was already sent (or being sent) on
 | 
				
			||||||
 | 
				
			|||||||
@ -493,6 +493,26 @@ impl AnyChannel {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn request_pause(&self) {
 | 
				
			||||||
 | 
					        let info = self.info();
 | 
				
			||||||
 | 
					        match self.info().dma {
 | 
				
			||||||
 | 
					            #[cfg(dma)]
 | 
				
			||||||
 | 
					            DmaInfo::Dma(r) => {
 | 
				
			||||||
 | 
					                // Disable the channel without overwriting the existing configuration
 | 
				
			||||||
 | 
					                r.st(info.num).cr().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_en(false);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            #[cfg(bdma)]
 | 
				
			||||||
 | 
					            DmaInfo::Bdma(r) => {
 | 
				
			||||||
 | 
					                // Disable the channel without overwriting the existing configuration
 | 
				
			||||||
 | 
					                r.ch(info.num).cr().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_en(false);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn is_running(&self) -> bool {
 | 
					    fn is_running(&self) -> bool {
 | 
				
			||||||
        let info = self.info();
 | 
					        let info = self.info();
 | 
				
			||||||
        match self.info().dma {
 | 
					        match self.info().dma {
 | 
				
			||||||
@ -667,12 +687,22 @@ impl<'a> Transfer<'a> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Request the transfer to stop.
 | 
					    /// Request the transfer to stop.
 | 
				
			||||||
 | 
					    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
 | 
				
			||||||
 | 
					    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
    pub fn request_stop(&mut self) {
 | 
					    pub fn request_stop(&mut self) {
 | 
				
			||||||
        self.channel.request_stop()
 | 
					        self.channel.request_stop()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Request the transfer to pause, keeping the existing configuration for this channel.
 | 
				
			||||||
 | 
					    /// To restart the transfer, call [`start`](Self::start) again.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
 | 
					    pub fn request_pause(&mut self) {
 | 
				
			||||||
 | 
					        self.channel.request_pause()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return whether this transfer is still running.
 | 
					    /// Return whether this transfer is still running.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// If this returns `false`, it can be because either the transfer finished, or
 | 
					    /// If this returns `false`, it can be because either the transfer finished, or
 | 
				
			||||||
@ -846,13 +876,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
 | 
				
			|||||||
        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
 | 
					        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Request DMA to stop.
 | 
					    /// Request the DMA to stop.
 | 
				
			||||||
 | 
					    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
 | 
				
			||||||
 | 
					    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
    pub fn request_stop(&mut self) {
 | 
					    pub fn request_stop(&mut self) {
 | 
				
			||||||
        self.channel.request_stop()
 | 
					        self.channel.request_stop()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Request the transfer to pause, keeping the existing configuration for this channel.
 | 
				
			||||||
 | 
					    /// To restart the transfer, call [`start`](Self::start) again.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
 | 
					    pub fn request_pause(&mut self) {
 | 
				
			||||||
 | 
					        self.channel.request_pause()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return whether DMA is still running.
 | 
					    /// Return whether DMA is still running.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// If this returns `false`, it can be because either the transfer finished, or
 | 
					    /// If this returns `false`, it can be because either the transfer finished, or
 | 
				
			||||||
@ -977,13 +1017,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
 | 
				
			|||||||
        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
 | 
					        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Request DMA to stop.
 | 
					    /// Request the DMA to stop.
 | 
				
			||||||
 | 
					    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
 | 
				
			||||||
 | 
					    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
    pub fn request_stop(&mut self) {
 | 
					    pub fn request_stop(&mut self) {
 | 
				
			||||||
        self.channel.request_stop()
 | 
					        self.channel.request_stop()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Request the transfer to pause, keeping the existing configuration for this channel.
 | 
				
			||||||
 | 
					    /// To restart the transfer, call [`start`](Self::start) again.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
 | 
				
			||||||
 | 
					    pub fn request_pause(&mut self) {
 | 
				
			||||||
 | 
					        self.channel.request_pause()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return whether DMA is still running.
 | 
					    /// Return whether DMA is still running.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// If this returns `false`, it can be because either the transfer finished, or
 | 
					    /// If this returns `false`, it can be because either the transfer finished, or
 | 
				
			||||||
 | 
				
			|||||||
@ -216,7 +216,10 @@ impl<'a> Transfer<'a> {
 | 
				
			|||||||
        data_size: WordSize,
 | 
					        data_size: WordSize,
 | 
				
			||||||
        _options: TransferOptions,
 | 
					        _options: TransferOptions,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        assert!(mem_len > 0 && mem_len <= 0xFFFF);
 | 
					        // BNDT is specified as bytes, not as number of transfers.
 | 
				
			||||||
 | 
					        let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
 | 
				
			||||||
 | 
					            panic!("DMA transfers may not be larger than 65535 bytes.");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let info = channel.info();
 | 
					        let info = channel.info();
 | 
				
			||||||
        let ch = info.dma.ch(info.num);
 | 
					        let ch = info.dma.ch(info.num);
 | 
				
			||||||
@ -226,9 +229,6 @@ impl<'a> Transfer<'a> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let this = Self { channel };
 | 
					        let this = Self { channel };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[cfg(dmamux)]
 | 
					 | 
				
			||||||
        super::dmamux::configure_dmamux(&*this.channel, request);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ch.cr().write(|w| w.set_reset(true));
 | 
					        ch.cr().write(|w| w.set_reset(true));
 | 
				
			||||||
        ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs
 | 
					        ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs
 | 
				
			||||||
        ch.llr().write(|_| {}); // no linked list
 | 
					        ch.llr().write(|_| {}); // no linked list
 | 
				
			||||||
@ -245,10 +245,8 @@ impl<'a> Transfer<'a> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            w.set_reqsel(request);
 | 
					            w.set_reqsel(request);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        ch.br1().write(|w| {
 | 
					        ch.tr3().write(|_| {}); // no address offsets.
 | 
				
			||||||
            // BNDT is specified as bytes, not as number of transfers.
 | 
					        ch.br1().write(|w| w.set_bndt(bndt));
 | 
				
			||||||
            w.set_bndt((mem_len * data_size.bytes()) as u16)
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match dir {
 | 
					        match dir {
 | 
				
			||||||
            Dir::MemoryToPeripheral => {
 | 
					            Dir::MemoryToPeripheral => {
 | 
				
			||||||
 | 
				
			|||||||
@ -177,6 +177,20 @@ pub unsafe trait PHY {
 | 
				
			|||||||
    fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
 | 
					    fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
 | 
				
			||||||
 | 
					    /// Directly expose the SMI interface used by the Ethernet driver.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This can be used to for example configure special PHY registers for compliance testing.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Safety
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Revert any temporary PHY register changes such as to enable test modes before handing
 | 
				
			||||||
 | 
					    /// the Ethernet device over to the networking stack otherwise things likely won't work.
 | 
				
			||||||
 | 
					    pub unsafe fn station_management(&mut self) -> &mut impl StationManagement {
 | 
				
			||||||
 | 
					        &mut self.station_management
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trait SealedInstance {
 | 
					trait SealedInstance {
 | 
				
			||||||
    fn regs() -> crate::pac::eth::Eth;
 | 
					    fn regs() -> crate::pac::eth::Eth;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,8 +34,10 @@ pub enum VoltageScale {
 | 
				
			|||||||
    Scale2,
 | 
					    Scale2,
 | 
				
			||||||
    Scale3,
 | 
					    Scale3,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#[cfg(any(stm32h7rs))]
 | 
					#[cfg(stm32h7rs)]
 | 
				
			||||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
 | 
					pub use crate::pac::pwr::vals::Vos as VoltageScale;
 | 
				
			||||||
 | 
					#[cfg(all(stm32h7rs, peri_usb_otg_hs))]
 | 
				
			||||||
 | 
					pub use crate::pac::rcc::vals::{Usbphycsel, Usbrefcksel};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
					#[derive(Clone, Copy, Eq, PartialEq)]
 | 
				
			||||||
pub enum HseMode {
 | 
					pub enum HseMode {
 | 
				
			||||||
@ -557,6 +559,27 @@ pub(crate) unsafe fn init(config: Config) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let rtc = config.ls.init();
 | 
					    let rtc = config.ls.init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(all(stm32h7rs, peri_usb_otg_hs))]
 | 
				
			||||||
 | 
					    let usb_refck = match config.mux.usbphycsel {
 | 
				
			||||||
 | 
					        Usbphycsel::HSE => hse,
 | 
				
			||||||
 | 
					        Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
 | 
				
			||||||
 | 
					        Usbphycsel::PLL3_Q => pll3.q,
 | 
				
			||||||
 | 
					        _ => None,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    #[cfg(all(stm32h7rs, peri_usb_otg_hs))]
 | 
				
			||||||
 | 
					    let usb_refck_sel = match usb_refck {
 | 
				
			||||||
 | 
					        Some(clk_val) => match clk_val {
 | 
				
			||||||
 | 
					            Hertz(16_000_000) => Usbrefcksel::MHZ16,
 | 
				
			||||||
 | 
					            Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
 | 
				
			||||||
 | 
					            Hertz(20_000_000) => Usbrefcksel::MHZ20,
 | 
				
			||||||
 | 
					            Hertz(24_000_000) => Usbrefcksel::MHZ24,
 | 
				
			||||||
 | 
					            Hertz(26_000_000) => Usbrefcksel::MHZ26,
 | 
				
			||||||
 | 
					            Hertz(32_000_000) => Usbrefcksel::MHZ32,
 | 
				
			||||||
 | 
					            _ => panic!("cannot select USBPHYC reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        None => Usbrefcksel::MHZ24,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(stm32h7)]
 | 
					    #[cfg(stm32h7)]
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        RCC.d1cfgr().modify(|w| {
 | 
					        RCC.d1cfgr().modify(|w| {
 | 
				
			||||||
@ -593,6 +616,11 @@ pub(crate) unsafe fn init(config: Config) {
 | 
				
			|||||||
            w.set_ppre4(config.apb4_pre);
 | 
					            w.set_ppre4(config.apb4_pre);
 | 
				
			||||||
            w.set_ppre5(config.apb5_pre);
 | 
					            w.set_ppre5(config.apb5_pre);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(peri_usb_otg_hs)]
 | 
				
			||||||
 | 
					        RCC.ahbperckselr().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_usbrefcksel(usb_refck_sel);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #[cfg(stm32h5)]
 | 
					    #[cfg(stm32h5)]
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -698,7 +726,9 @@ pub(crate) unsafe fn init(config: Config) {
 | 
				
			|||||||
        #[cfg(stm32h7rs)]
 | 
					        #[cfg(stm32h7rs)]
 | 
				
			||||||
        clk48mohci: None, // TODO
 | 
					        clk48mohci: None, // TODO
 | 
				
			||||||
        #[cfg(stm32h7rs)]
 | 
					        #[cfg(stm32h7rs)]
 | 
				
			||||||
        usb: None, // TODO
 | 
					        hse_div_2: hse.map(|clk| clk / 2u32),
 | 
				
			||||||
 | 
					        #[cfg(stm32h7rs)]
 | 
				
			||||||
 | 
					        usb: Some(Hertz(48_000_000)),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -769,7 +799,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
 | 
				
			|||||||
        if num == 0 {
 | 
					        if num == 0 {
 | 
				
			||||||
            // on PLL1, DIVP must be even for most series.
 | 
					            // on PLL1, DIVP must be even for most series.
 | 
				
			||||||
            // The enum value is 1 less than the divider, so check it's odd.
 | 
					            // The enum value is 1 less than the divider, so check it's odd.
 | 
				
			||||||
            #[cfg(not(pwr_h7rm0468))]
 | 
					            #[cfg(not(any(pwr_h7rm0468, stm32h7rs)))]
 | 
				
			||||||
            assert!(div.to_bits() % 2 == 1);
 | 
					            assert!(div.to_bits() % 2 == 1);
 | 
				
			||||||
            #[cfg(pwr_h7rm0468)]
 | 
					            #[cfg(pwr_h7rm0468)]
 | 
				
			||||||
            assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0);
 | 
					            assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0);
 | 
				
			||||||
 | 
				
			|||||||
@ -311,51 +311,29 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself.
 | 
				
			||||||
    fn set_word_size(&mut self, word_size: word_impl::Config) {
 | 
					    fn set_word_size(&mut self, word_size: word_impl::Config) {
 | 
				
			||||||
        if self.current_word_size == word_size {
 | 
					        if self.current_word_size == word_size {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.info.regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_spe(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[cfg(any(spi_v1, spi_f1))]
 | 
					        #[cfg(any(spi_v1, spi_f1))]
 | 
				
			||||||
        {
 | 
					        self.info.regs.cr1().modify(|reg| {
 | 
				
			||||||
            self.info.regs.cr1().modify(|reg| {
 | 
					            reg.set_dff(word_size);
 | 
				
			||||||
                reg.set_spe(false);
 | 
					        });
 | 
				
			||||||
                reg.set_dff(word_size)
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            self.info.regs.cr1().modify(|reg| {
 | 
					 | 
				
			||||||
                reg.set_spe(true);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        #[cfg(spi_v2)]
 | 
					        #[cfg(spi_v2)]
 | 
				
			||||||
        {
 | 
					        self.info.regs.cr2().modify(|w| {
 | 
				
			||||||
            self.info.regs.cr1().modify(|w| {
 | 
					            w.set_frxth(word_size.1);
 | 
				
			||||||
                w.set_spe(false);
 | 
					            w.set_ds(word_size.0);
 | 
				
			||||||
            });
 | 
					        });
 | 
				
			||||||
            self.info.regs.cr2().modify(|w| {
 | 
					 | 
				
			||||||
                w.set_frxth(word_size.1);
 | 
					 | 
				
			||||||
                w.set_ds(word_size.0);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            self.info.regs.cr1().modify(|w| {
 | 
					 | 
				
			||||||
                w.set_spe(true);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
        {
 | 
					        self.info.regs.cfg1().modify(|w| {
 | 
				
			||||||
            self.info.regs.cr1().modify(|w| {
 | 
					            w.set_dsize(word_size);
 | 
				
			||||||
                w.set_csusp(true);
 | 
					        });
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            while self.info.regs.sr().read().eot() {}
 | 
					 | 
				
			||||||
            self.info.regs.cr1().modify(|w| {
 | 
					 | 
				
			||||||
                w.set_spe(false);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            self.info.regs.cfg1().modify(|w| {
 | 
					 | 
				
			||||||
                w.set_dsize(word_size);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            self.info.regs.cr1().modify(|w| {
 | 
					 | 
				
			||||||
                w.set_csusp(false);
 | 
					 | 
				
			||||||
                w.set_spe(true);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.current_word_size = word_size;
 | 
					        self.current_word_size = word_size;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -365,9 +343,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
					        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
				
			||||||
        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        for word in words.iter() {
 | 
					        for word in words.iter() {
 | 
				
			||||||
            // this cannot use `transfer_word` because on SPIv2 and higher,
 | 
					            // this cannot use `transfer_word` because on SPIv2 and higher,
 | 
				
			||||||
            // the SPI RX state machine hangs if no physical pin is connected to the SCK AF.
 | 
					            // the SPI RX state machine hangs if no physical pin is connected to the SCK AF.
 | 
				
			||||||
@ -402,9 +380,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
					        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
				
			||||||
        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        for word in words.iter_mut() {
 | 
					        for word in words.iter_mut() {
 | 
				
			||||||
            *word = transfer_word(self.info.regs, W::default())?;
 | 
					            *word = transfer_word(self.info.regs, W::default())?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -418,9 +396,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
					        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
				
			||||||
        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        for word in words.iter_mut() {
 | 
					        for word in words.iter_mut() {
 | 
				
			||||||
            *word = transfer_word(self.info.regs, *word)?;
 | 
					            *word = transfer_word(self.info.regs, *word)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -437,9 +415,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
					        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
 | 
				
			||||||
        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(false));
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
					        self.info.regs.cr1().modify(|w| w.set_spe(true));
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        let len = read.len().max(write.len());
 | 
					        let len = read.len().max(write.len());
 | 
				
			||||||
        for i in 0..len {
 | 
					        for i in 0..len {
 | 
				
			||||||
            let wb = write.get(i).copied().unwrap_or_default();
 | 
					            let wb = write.get(i).copied().unwrap_or_default();
 | 
				
			||||||
@ -648,10 +626,10 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        self.info.regs.cr1().modify(|w| {
 | 
					        self.info.regs.cr1().modify(|w| {
 | 
				
			||||||
            w.set_spe(false);
 | 
					            w.set_spe(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let tx_dst = self.info.regs.tx_ptr();
 | 
					        let tx_dst = self.info.regs.tx_ptr();
 | 
				
			||||||
        let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) };
 | 
					        let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) };
 | 
				
			||||||
@ -685,6 +663,8 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
            w.set_spe(false);
 | 
					            w.set_spe(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let comm = regs.cfg2().modify(|w| {
 | 
					        let comm = regs.cfg2().modify(|w| {
 | 
				
			||||||
            let prev = w.comm();
 | 
					            let prev = w.comm();
 | 
				
			||||||
            w.set_comm(vals::Comm::RECEIVER);
 | 
					            w.set_comm(vals::Comm::RECEIVER);
 | 
				
			||||||
@ -707,7 +687,6 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
        let rx_src = regs.rx_ptr();
 | 
					        let rx_src = regs.rx_ptr();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for mut chunk in data.chunks_mut(u16::max_value().into()) {
 | 
					        for mut chunk in data.chunks_mut(u16::max_value().into()) {
 | 
				
			||||||
            self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
            set_rxdmaen(regs, true);
 | 
					            set_rxdmaen(regs, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let tsize = chunk.len();
 | 
					            let tsize = chunk.len();
 | 
				
			||||||
@ -765,12 +744,12 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.info.regs.cr1().modify(|w| {
 | 
					        self.info.regs.cr1().modify(|w| {
 | 
				
			||||||
            w.set_spe(false);
 | 
					            w.set_spe(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // SPIv3 clears rxfifo on SPE=0
 | 
					        // SPIv3 clears rxfifo on SPE=0
 | 
				
			||||||
        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
					        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
@ -783,7 +762,7 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
        let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) };
 | 
					        let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let tx_dst = self.info.regs.tx_ptr();
 | 
					        let tx_dst = self.info.regs.tx_ptr();
 | 
				
			||||||
        let clock_byte = 0x00u8;
 | 
					        let clock_byte = W::default();
 | 
				
			||||||
        let tx_f = unsafe {
 | 
					        let tx_f = unsafe {
 | 
				
			||||||
            self.tx_dma
 | 
					            self.tx_dma
 | 
				
			||||||
                .as_mut()
 | 
					                .as_mut()
 | 
				
			||||||
@ -813,11 +792,12 @@ impl<'d> Spi<'d, Async> {
 | 
				
			|||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.set_word_size(W::CONFIG);
 | 
					 | 
				
			||||||
        self.info.regs.cr1().modify(|w| {
 | 
					        self.info.regs.cr1().modify(|w| {
 | 
				
			||||||
            w.set_spe(false);
 | 
					            w.set_spe(false);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.set_word_size(W::CONFIG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // SPIv3 clears rxfifo on SPE=0
 | 
					        // SPIv3 clears rxfifo on SPE=0
 | 
				
			||||||
        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
					        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
        flush_rx_fifo(self.info.regs);
 | 
					        flush_rx_fifo(self.info.regs);
 | 
				
			||||||
@ -1195,7 +1175,7 @@ trait SealedWord {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Word sizes usable for SPI.
 | 
					/// Word sizes usable for SPI.
 | 
				
			||||||
#[allow(private_bounds)]
 | 
					#[allow(private_bounds)]
 | 
				
			||||||
pub trait Word: word::Word + SealedWord {}
 | 
					pub trait Word: word::Word + SealedWord + Default {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
macro_rules! impl_word {
 | 
					macro_rules! impl_word {
 | 
				
			||||||
    ($T:ty, $config:expr) => {
 | 
					    ($T:ty, $config:expr) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -71,34 +71,19 @@ impl<'d> UartRx<'d, Async> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d> RingBufferedUartRx<'d> {
 | 
					impl<'d> RingBufferedUartRx<'d> {
 | 
				
			||||||
    /// Clear the ring buffer and start receiving in the background
 | 
					 | 
				
			||||||
    pub fn start(&mut self) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        // Clear the ring buffer so that it is ready to receive data
 | 
					 | 
				
			||||||
        self.ring_buf.clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.setup_uart();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn stop(&mut self, err: Error) -> Result<usize, Error> {
 | 
					 | 
				
			||||||
        self.teardown_uart();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Err(err)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Reconfigure the driver
 | 
					    /// Reconfigure the driver
 | 
				
			||||||
    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
 | 
					    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
 | 
				
			||||||
        reconfigure(self.info, self.kernel_clock, config)
 | 
					        reconfigure(self.info, self.kernel_clock, config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Start uart background receive
 | 
					    /// Configure and start the DMA backed UART receiver
 | 
				
			||||||
    fn setup_uart(&mut self) {
 | 
					    ///
 | 
				
			||||||
        // fence before starting DMA.
 | 
					    /// Note: This is also done automatically by [`read()`] if required.
 | 
				
			||||||
 | 
					    pub fn start_uart(&mut self) {
 | 
				
			||||||
 | 
					        // Clear the buffer so that it is ready to receive data
 | 
				
			||||||
        compiler_fence(Ordering::SeqCst);
 | 
					        compiler_fence(Ordering::SeqCst);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // start the dma controller
 | 
					 | 
				
			||||||
        self.ring_buf.start();
 | 
					        self.ring_buf.start();
 | 
				
			||||||
 | 
					        self.ring_buf.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let r = self.info.regs;
 | 
					        let r = self.info.regs;
 | 
				
			||||||
        // clear all interrupts and DMA Rx Request
 | 
					        // clear all interrupts and DMA Rx Request
 | 
				
			||||||
@ -118,9 +103,9 @@ impl<'d> RingBufferedUartRx<'d> {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Stop uart background receive
 | 
					    /// Stop DMA backed UART receiver
 | 
				
			||||||
    fn teardown_uart(&mut self) {
 | 
					    fn stop_uart(&mut self) {
 | 
				
			||||||
        self.ring_buf.request_stop();
 | 
					        self.ring_buf.request_pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let r = self.info.regs;
 | 
					        let r = self.info.regs;
 | 
				
			||||||
        // clear all interrupts and DMA Rx Request
 | 
					        // clear all interrupts and DMA Rx Request
 | 
				
			||||||
@ -153,13 +138,15 @@ impl<'d> RingBufferedUartRx<'d> {
 | 
				
			|||||||
    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
					    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
				
			||||||
        let r = self.info.regs;
 | 
					        let r = self.info.regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Start background receive if it was not already started
 | 
					        // Start DMA and Uart if it was not already started,
 | 
				
			||||||
 | 
					        // otherwise check for errors in status register.
 | 
				
			||||||
 | 
					        let sr = clear_idle_flag(r);
 | 
				
			||||||
        if !r.cr3().read().dmar() {
 | 
					        if !r.cr3().read().dmar() {
 | 
				
			||||||
            self.start()?;
 | 
					            self.start_uart();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            check_for_errors(sr)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        check_for_errors(clear_idle_flag(r))?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            match self.ring_buf.read(buf) {
 | 
					            match self.ring_buf.read(buf) {
 | 
				
			||||||
                Ok((0, _)) => {}
 | 
					                Ok((0, _)) => {}
 | 
				
			||||||
@ -167,14 +154,16 @@ impl<'d> RingBufferedUartRx<'d> {
 | 
				
			|||||||
                    return Ok(len);
 | 
					                    return Ok(len);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Err(_) => {
 | 
					                Err(_) => {
 | 
				
			||||||
                    return self.stop(Error::Overrun);
 | 
					                    self.stop_uart();
 | 
				
			||||||
 | 
					                    return Err(Error::Overrun);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self.wait_for_data_or_idle().await {
 | 
					            match self.wait_for_data_or_idle().await {
 | 
				
			||||||
                Ok(_) => {}
 | 
					                Ok(_) => {}
 | 
				
			||||||
                Err(err) => {
 | 
					                Err(err) => {
 | 
				
			||||||
                    return self.stop(err);
 | 
					                    self.stop_uart();
 | 
				
			||||||
 | 
					                    return Err(err);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -228,7 +217,7 @@ impl<'d> RingBufferedUartRx<'d> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl Drop for RingBufferedUartRx<'_> {
 | 
					impl Drop for RingBufferedUartRx<'_> {
 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        self.teardown_uart();
 | 
					        self.stop_uart();
 | 
				
			||||||
        self.rx.as_ref().map(|x| x.set_as_disconnected());
 | 
					        self.rx.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
        self.rts.as_ref().map(|x| x.set_as_disconnected());
 | 
					        self.rts.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
        super::drop_tx_rx(self.info, self.state);
 | 
					        super::drop_tx_rx(self.info, self.state);
 | 
				
			||||||
 | 
				
			|||||||
@ -13,9 +13,19 @@ fn common_init<T: Instance>() {
 | 
				
			|||||||
    // Check the USB clock is enabled and running at exactly 48 MHz.
 | 
					    // Check the USB clock is enabled and running at exactly 48 MHz.
 | 
				
			||||||
    // frequency() will panic if not enabled
 | 
					    // frequency() will panic if not enabled
 | 
				
			||||||
    let freq = T::frequency();
 | 
					    let freq = T::frequency();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally
 | 
				
			||||||
 | 
					    #[cfg(stm32h7rs)]
 | 
				
			||||||
 | 
					    if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
 | 
				
			||||||
 | 
					        panic!(
 | 
				
			||||||
 | 
					            "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.",
 | 
				
			||||||
 | 
					            freq.0
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    // Check frequency is within the 0.25% tolerance allowed by the spec.
 | 
					    // Check frequency is within the 0.25% tolerance allowed by the spec.
 | 
				
			||||||
    // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
 | 
					    // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
 | 
				
			||||||
    // has tight clock restrictions due to something else (like audio).
 | 
					    // has tight clock restrictions due to something else (like audio).
 | 
				
			||||||
 | 
					    #[cfg(not(stm32h7rs))]
 | 
				
			||||||
    if freq.0.abs_diff(48_000_000) > 120_000 {
 | 
					    if freq.0.abs_diff(48_000_000) > 120_000 {
 | 
				
			||||||
        panic!(
 | 
					        panic!(
 | 
				
			||||||
            "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
 | 
					            "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
 | 
				
			||||||
@ -48,6 +58,26 @@ fn common_init<T: Instance>() {
 | 
				
			|||||||
        while !crate::pac::PWR.cr3().read().usb33rdy() {}
 | 
					        while !crate::pac::PWR.cr3().read().usb33rdy() {}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(stm32h7rs)]
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // If true, VDD33USB is generated by internal regulator from VDD50USB
 | 
				
			||||||
 | 
					        // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
 | 
				
			||||||
 | 
					        // TODO: unhardcode
 | 
				
			||||||
 | 
					        let internal_regulator = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Enable USB power
 | 
				
			||||||
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
 | 
					            crate::pac::PWR.csr2().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_usbregen(internal_regulator);
 | 
				
			||||||
 | 
					                w.set_usb33den(true);
 | 
				
			||||||
 | 
					                w.set_usbhsregen(true);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Wait for USB power to stabilize
 | 
				
			||||||
 | 
					        while !crate::pac::PWR.csr2().read().usb33rdy() {}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(stm32u5)]
 | 
					    #[cfg(stm32u5)]
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Enable USB power
 | 
					        // Enable USB power
 | 
				
			||||||
 | 
				
			|||||||
@ -97,6 +97,45 @@ impl<'d, T: Instance> Driver<'d, T> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Initializes USB OTG peripheral with internal High-Speed PHY.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Arguments
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.
 | 
				
			||||||
 | 
					    /// Must be large enough to fit all OUT endpoint max packet sizes.
 | 
				
			||||||
 | 
					    /// Endpoint allocation will fail if it is too small.
 | 
				
			||||||
 | 
					    pub fn new_hs(
 | 
				
			||||||
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
 | 
				
			||||||
 | 
					        dp: impl Peripheral<P = impl DpPin<T>> + 'd,
 | 
				
			||||||
 | 
					        dm: impl Peripheral<P = impl DmPin<T>> + 'd,
 | 
				
			||||||
 | 
					        ep_out_buffer: &'d mut [u8],
 | 
				
			||||||
 | 
					        config: Config,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(dp, dm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
				
			||||||
 | 
					        dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let regs = T::regs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let instance = OtgInstance {
 | 
				
			||||||
 | 
					            regs,
 | 
				
			||||||
 | 
					            state: T::state(),
 | 
				
			||||||
 | 
					            fifo_depth_words: T::FIFO_DEPTH_WORDS,
 | 
				
			||||||
 | 
					            extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,
 | 
				
			||||||
 | 
					            endpoint_count: T::ENDPOINT_COUNT,
 | 
				
			||||||
 | 
					            phy_type: PhyType::InternalHighSpeed,
 | 
				
			||||||
 | 
					            quirk_setup_late_cnak: quirk_setup_late_cnak(regs),
 | 
				
			||||||
 | 
					            calculate_trdt_fn: calculate_trdt::<T>,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            inner: OtgDriver::new(ep_out_buffer, instance, config),
 | 
				
			||||||
 | 
					            phantom: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode).
 | 
					    /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode).
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Arguments
 | 
					    /// # Arguments
 | 
				
			||||||
@ -272,6 +311,19 @@ impl<'d, T: Instance> Bus<'d, T> {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(stm32h7rs)]
 | 
				
			||||||
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
 | 
					            let rcc = crate::pac::RCC;
 | 
				
			||||||
 | 
					            rcc.ahb1enr().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_usbphycen(true);
 | 
				
			||||||
 | 
					                w.set_usb_otg_hsen(true);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            rcc.ahb1lpenr().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_usbphyclpen(true);
 | 
				
			||||||
 | 
					                w.set_usb_otg_hslpen(true);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let r = T::regs();
 | 
					        let r = T::regs();
 | 
				
			||||||
        let core_id = r.cid().read().0;
 | 
					        let core_id = r.cid().read().0;
 | 
				
			||||||
        trace!("Core id {:08x}", core_id);
 | 
					        trace!("Core id {:08x}", core_id);
 | 
				
			||||||
@ -286,6 +338,7 @@ impl<'d, T: Instance> Bus<'d, T> {
 | 
				
			|||||||
        match core_id {
 | 
					        match core_id {
 | 
				
			||||||
            0x0000_1200 | 0x0000_1100 => self.inner.config_v1(),
 | 
					            0x0000_1200 | 0x0000_1100 => self.inner.config_v1(),
 | 
				
			||||||
            0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),
 | 
					            0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),
 | 
				
			||||||
 | 
					            0x0000_5000 => self.inner.config_v5(),
 | 
				
			||||||
            _ => unimplemented!("Unknown USB core id {:X}", core_id),
 | 
					            _ => unimplemented!("Unknown USB core id {:X}", core_id),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -501,7 +554,7 @@ fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 {
 | 
				
			|||||||
    match speed {
 | 
					    match speed {
 | 
				
			||||||
        Dspd::HIGH_SPEED => {
 | 
					        Dspd::HIGH_SPEED => {
 | 
				
			||||||
            // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
 | 
					            // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
 | 
				
			||||||
            if ahb_freq >= 30_000_000 {
 | 
					            if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) {
 | 
				
			||||||
                0x9
 | 
					                0x9
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                panic!("AHB frequency is too low")
 | 
					                panic!("AHB frequency is too low")
 | 
				
			||||||
 | 
				
			|||||||
@ -194,6 +194,25 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> crate::pubsub::PubSubBehavior<T>
 | 
				
			||||||
 | 
					    for PubSubChannel<M, T, CAP, SUBS, PUBS>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn publish_immediate(&self, message: T) {
 | 
				
			||||||
 | 
					        self.inner.lock(|s| {
 | 
				
			||||||
 | 
					            let mut s = s.borrow_mut();
 | 
				
			||||||
 | 
					            s.publish_immediate(message)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn capacity(&self) -> usize {
 | 
				
			||||||
 | 
					        self.capacity()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn is_full(&self) -> bool {
 | 
				
			||||||
 | 
					        self.is_full()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> SealedPubSubBehavior<T>
 | 
					impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> SealedPubSubBehavior<T>
 | 
				
			||||||
    for PubSubChannel<M, T, CAP, SUBS, PUBS>
 | 
					    for PubSubChannel<M, T, CAP, SUBS, PUBS>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -246,13 +265,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn publish_immediate(&self, message: T) {
 | 
					 | 
				
			||||||
        self.inner.lock(|s| {
 | 
					 | 
				
			||||||
            let mut s = s.borrow_mut();
 | 
					 | 
				
			||||||
            s.publish_immediate(message)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn unregister_subscriber(&self, subscriber_next_message_id: u64) {
 | 
					    fn unregister_subscriber(&self, subscriber_next_message_id: u64) {
 | 
				
			||||||
        self.inner.lock(|s| {
 | 
					        self.inner.lock(|s| {
 | 
				
			||||||
            let mut s = s.borrow_mut();
 | 
					            let mut s = s.borrow_mut();
 | 
				
			||||||
@ -267,10 +279,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn capacity(&self) -> usize {
 | 
					 | 
				
			||||||
        self.capacity()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn free_capacity(&self) -> usize {
 | 
					    fn free_capacity(&self) -> usize {
 | 
				
			||||||
        self.free_capacity()
 | 
					        self.free_capacity()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -286,10 +294,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
 | 
				
			|||||||
    fn is_empty(&self) -> bool {
 | 
					    fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.is_empty()
 | 
					        self.is_empty()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn is_full(&self) -> bool {
 | 
					 | 
				
			||||||
        self.is_full()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Internal state for the PubSub channel
 | 
					/// Internal state for the PubSub channel
 | 
				
			||||||
@ -445,8 +449,6 @@ pub enum Error {
 | 
				
			|||||||
    MaximumPublishersReached,
 | 
					    MaximumPublishersReached,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// 'Middle level' behaviour of the pubsub channel.
 | 
					 | 
				
			||||||
/// This trait is used so that Sub and Pub can be generic over the channel.
 | 
					 | 
				
			||||||
trait SealedPubSubBehavior<T> {
 | 
					trait SealedPubSubBehavior<T> {
 | 
				
			||||||
    /// Try to get a message from the queue with the given message id.
 | 
					    /// Try to get a message from the queue with the given message id.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
@ -462,12 +464,6 @@ trait SealedPubSubBehavior<T> {
 | 
				
			|||||||
    /// If the queue is full and a context is given, then its waker is registered in the publisher wakers.
 | 
					    /// If the queue is full and a context is given, then its waker is registered in the publisher wakers.
 | 
				
			||||||
    fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>;
 | 
					    fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Publish a message immediately
 | 
					 | 
				
			||||||
    fn publish_immediate(&self, message: T);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the maximum number of elements the channel can hold.
 | 
					 | 
				
			||||||
    fn capacity(&self) -> usize;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the free capacity of the channel.
 | 
					    /// Returns the free capacity of the channel.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This is equivalent to `capacity() - len()`
 | 
					    /// This is equivalent to `capacity() - len()`
 | 
				
			||||||
@ -482,9 +478,6 @@ trait SealedPubSubBehavior<T> {
 | 
				
			|||||||
    /// Returns whether the channel is empty.
 | 
					    /// Returns whether the channel is empty.
 | 
				
			||||||
    fn is_empty(&self) -> bool;
 | 
					    fn is_empty(&self) -> bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns whether the channel is full.
 | 
					 | 
				
			||||||
    fn is_full(&self) -> bool;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Let the channel know that a subscriber has dropped
 | 
					    /// Let the channel know that a subscriber has dropped
 | 
				
			||||||
    fn unregister_subscriber(&self, subscriber_next_message_id: u64);
 | 
					    fn unregister_subscriber(&self, subscriber_next_message_id: u64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -495,9 +488,16 @@ trait SealedPubSubBehavior<T> {
 | 
				
			|||||||
/// 'Middle level' behaviour of the pubsub channel.
 | 
					/// 'Middle level' behaviour of the pubsub channel.
 | 
				
			||||||
/// This trait is used so that Sub and Pub can be generic over the channel.
 | 
					/// This trait is used so that Sub and Pub can be generic over the channel.
 | 
				
			||||||
#[allow(private_bounds)]
 | 
					#[allow(private_bounds)]
 | 
				
			||||||
pub trait PubSubBehavior<T>: SealedPubSubBehavior<T> {}
 | 
					pub trait PubSubBehavior<T>: SealedPubSubBehavior<T> {
 | 
				
			||||||
 | 
					    /// Publish a message immediately
 | 
				
			||||||
 | 
					    fn publish_immediate(&self, message: T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T, C: SealedPubSubBehavior<T>> PubSubBehavior<T> for C {}
 | 
					    /// Returns the maximum number of elements the channel can hold.
 | 
				
			||||||
 | 
					    fn capacity(&self) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns whether the channel is full.
 | 
				
			||||||
 | 
					    fn is_full(&self) -> bool;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The result of the subscriber wait procedure
 | 
					/// The result of the subscriber wait procedure
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
				
			|||||||
@ -584,6 +584,29 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Applies configuration specific to
 | 
				
			||||||
 | 
					    /// Core ID 0x0000_5000
 | 
				
			||||||
 | 
					    pub fn config_v5(&mut self) {
 | 
				
			||||||
 | 
					        let r = self.instance.regs;
 | 
				
			||||||
 | 
					        let phy_type = self.instance.phy_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if phy_type == PhyType::InternalHighSpeed {
 | 
				
			||||||
 | 
					            r.gccfg_v3().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_vbvaloven(!self.config.vbus_detection);
 | 
				
			||||||
 | 
					                w.set_vbvaloval(!self.config.vbus_detection);
 | 
				
			||||||
 | 
					                w.set_vbden(self.config.vbus_detection);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            r.gotgctl().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_bvaloen(!self.config.vbus_detection);
 | 
				
			||||||
 | 
					                w.set_bvaloval(!self.config.vbus_detection);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            r.gccfg_v3().modify(|w| {
 | 
				
			||||||
 | 
					                w.set_vbden(self.config.vbus_detection);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn init(&mut self) {
 | 
					    fn init(&mut self) {
 | 
				
			||||||
        let r = self.instance.regs;
 | 
					        let r = self.instance.regs;
 | 
				
			||||||
        let phy_type = self.instance.phy_type;
 | 
					        let phy_type = self.instance.phy_type;
 | 
				
			||||||
 | 
				
			|||||||
@ -186,6 +186,11 @@ impl Otg {
 | 
				
			|||||||
    pub const fn gccfg_v2(self) -> Reg<regs::GccfgV2, RW> {
 | 
					    pub const fn gccfg_v2(self) -> Reg<regs::GccfgV2, RW> {
 | 
				
			||||||
        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }
 | 
					        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    #[doc = "General core configuration register, for core_id 0x0000_5xxx"]
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub const fn gccfg_v3(self) -> Reg<regs::GccfgV3, RW> {
 | 
				
			||||||
 | 
					        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    #[doc = "Core ID register"]
 | 
					    #[doc = "Core ID register"]
 | 
				
			||||||
    #[inline(always)]
 | 
					    #[inline(always)]
 | 
				
			||||||
    pub const fn cid(self) -> Reg<regs::Cid, RW> {
 | 
					    pub const fn cid(self) -> Reg<regs::Cid, RW> {
 | 
				
			||||||
@ -1831,6 +1836,172 @@ pub mod regs {
 | 
				
			|||||||
            GccfgV2(0)
 | 
					            GccfgV2(0)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    #[doc = "OTG general core configuration register."]
 | 
				
			||||||
 | 
					    #[repr(transparent)]
 | 
				
			||||||
 | 
					    #[derive(Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
 | 
					    pub struct GccfgV3(pub u32);
 | 
				
			||||||
 | 
					    impl GccfgV3 {
 | 
				
			||||||
 | 
					        #[doc = "Charger detection, result of the current mode (primary or secondary)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn chgdet(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 0usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Charger detection, result of the current mode (primary or secondary)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_chgdet(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn fsvplus(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 1usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_fsvplus(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn fsvminus(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 2usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_fsvminus(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn sessvld(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 3usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_sessvld(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP behavior enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn hcdpen(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 16usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP behavior enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_hcdpen(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP port voltage detector enable on DP."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn hcdpdeten(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 17usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP port voltage detector enable on DP."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_hcdpdeten(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP port Voltage source enable on DM."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn hvdmsrcen(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 18usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Host CDP port Voltage source enable on DM."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_hvdmsrcen(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Data Contact Detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn dcden(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 19usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Data Contact Detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_dcden(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Primary detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn pden(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 20usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Primary detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_pden(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn vbden(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 21usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_vbden(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Secondary detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn sden(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 22usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Secondary detection enable."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_sden(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Software override value of the VBUS B-session detection."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn vbvaloval(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 23usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Software override value of the VBUS B-session detection."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_vbvaloval(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Enables a software override of the VBUS B-session detection."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn vbvaloven(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 24usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Enables a software override of the VBUS B-session detection."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_vbvaloven(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub const fn forcehostpd(&self) -> bool {
 | 
				
			||||||
 | 
					            let val = (self.0 >> 25usize) & 0x01;
 | 
				
			||||||
 | 
					            val != 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."]
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        pub fn set_forcehostpd(&mut self, val: bool) {
 | 
				
			||||||
 | 
					            self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    impl Default for GccfgV3 {
 | 
				
			||||||
 | 
					        #[inline(always)]
 | 
				
			||||||
 | 
					        fn default() -> GccfgV3 {
 | 
				
			||||||
 | 
					            GccfgV3(0)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    #[doc = "I2C access register"]
 | 
					    #[doc = "I2C access register"]
 | 
				
			||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
    #[derive(Copy, Clone, Eq, PartialEq)]
 | 
					    #[derive(Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
 | 
				
			|||||||
@ -31,4 +31,7 @@ fn main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    println!("cargo:rustc-link-arg-bins=--nmagic");
 | 
					    println!("cargo:rustc-link-arg-bins=--nmagic");
 | 
				
			||||||
    println!("cargo:rustc-link-arg-bins=-Tlink.x");
 | 
					    println!("cargo:rustc-link-arg-bins=-Tlink.x");
 | 
				
			||||||
 | 
					    if env::var("CARGO_FEATURE_DEFMT").is_ok() {
 | 
				
			||||||
 | 
					        println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,9 @@
 | 
				
			|||||||
#![no_main]
 | 
					#![no_main]
 | 
				
			||||||
#![macro_use]
 | 
					#![macro_use]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "defmt")]
 | 
				
			||||||
 | 
					use defmt_rtt as _;
 | 
				
			||||||
 | 
					use embassy_boot::State;
 | 
				
			||||||
use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig};
 | 
					use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig};
 | 
				
			||||||
use embassy_embedded_hal::adapter::BlockingAsync;
 | 
					use embassy_embedded_hal::adapter::BlockingAsync;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
@ -22,6 +25,7 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let mut button = Input::new(p.P0_11, Pull::Up);
 | 
					    let mut button = Input::new(p.P0_11, Pull::Up);
 | 
				
			||||||
    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);
 | 
					    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);
 | 
				
			||||||
 | 
					    let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);
 | 
					    //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);
 | 
				
			||||||
    //let mut button = Input::new(p.P1_02, Pull::Up);
 | 
					    //let mut button = Input::new(p.P1_02, Pull::Up);
 | 
				
			||||||
@ -53,6 +57,13 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc);
 | 
					    let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc);
 | 
				
			||||||
    let mut magic = [0; 4];
 | 
					    let mut magic = [0; 4];
 | 
				
			||||||
    let mut updater = FirmwareUpdater::new(config, &mut magic);
 | 
					    let mut updater = FirmwareUpdater::new(config, &mut magic);
 | 
				
			||||||
 | 
					    let state = updater.get_state().await.unwrap();
 | 
				
			||||||
 | 
					    if state == State::Revert {
 | 
				
			||||||
 | 
					        led_reverted.set_low();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        led_reverted.set_high();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        led.set_low();
 | 
					        led.set_low();
 | 
				
			||||||
        button.wait_for_any_edge().await;
 | 
					        button.wait_for_any_edge().await;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										155
									
								
								examples/rp/src/bin/pio_onewire.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								examples/rp/src/bin/pio_onewire.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_rp::bind_interrupts;
 | 
				
			||||||
 | 
					use embassy_rp::peripherals::PIO0;
 | 
				
			||||||
 | 
					use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine};
 | 
				
			||||||
 | 
					use embassy_time::Timer;
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bind_interrupts!(struct Irqs {
 | 
				
			||||||
 | 
					    PIO0_IRQ_0 => InterruptHandler<PIO0>;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let p = embassy_rp::init(Default::default());
 | 
				
			||||||
 | 
					    let mut pio = Pio::new(p.PIO0, Irqs);
 | 
				
			||||||
 | 
					    let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        sensor.start().await; // Start a new measurement
 | 
				
			||||||
 | 
					        Timer::after_secs(1).await; // Allow 1s for the measurement to finish
 | 
				
			||||||
 | 
					        match sensor.temperature().await {
 | 
				
			||||||
 | 
					            Ok(temp) => info!("temp = {:?} deg C", temp),
 | 
				
			||||||
 | 
					            _ => error!("sensor error"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Timer::after_secs(1).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// DS18B20 temperature sensor driver
 | 
				
			||||||
 | 
					pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
 | 
				
			||||||
 | 
					    sm: StateMachine<'d, PIO, SM>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
 | 
				
			||||||
 | 
					    /// Create a new instance the driver
 | 
				
			||||||
 | 
					    pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self {
 | 
				
			||||||
 | 
					        let prg = pio_proc::pio_asm!(
 | 
				
			||||||
 | 
					            r#"
 | 
				
			||||||
 | 
					                .wrap_target
 | 
				
			||||||
 | 
					                    again:
 | 
				
			||||||
 | 
					                        pull block
 | 
				
			||||||
 | 
					                        mov x, osr
 | 
				
			||||||
 | 
					                        jmp !x, read
 | 
				
			||||||
 | 
					                        write:
 | 
				
			||||||
 | 
					                            set pindirs, 1 
 | 
				
			||||||
 | 
					                            set pins, 0  
 | 
				
			||||||
 | 
					                            loop1: 
 | 
				
			||||||
 | 
					                                jmp x--,loop1
 | 
				
			||||||
 | 
					                            set pindirs, 0 [31]
 | 
				
			||||||
 | 
					                            wait 1 pin 0 [31]
 | 
				
			||||||
 | 
					                            pull block
 | 
				
			||||||
 | 
					                            mov x, osr
 | 
				
			||||||
 | 
					                            bytes1:
 | 
				
			||||||
 | 
					                                pull block
 | 
				
			||||||
 | 
					                                set y, 7    
 | 
				
			||||||
 | 
					                                set pindirs, 1 
 | 
				
			||||||
 | 
					                                bit1:
 | 
				
			||||||
 | 
					                                    set pins, 0 [1]
 | 
				
			||||||
 | 
					                                    out pins,1 [31]
 | 
				
			||||||
 | 
					                                    set pins, 1 [20]
 | 
				
			||||||
 | 
					                                    jmp y--,bit1
 | 
				
			||||||
 | 
					                                jmp x--,bytes1
 | 
				
			||||||
 | 
					                            set pindirs, 0 [31]
 | 
				
			||||||
 | 
					                            jmp again
 | 
				
			||||||
 | 
					                        read:
 | 
				
			||||||
 | 
					                            pull block
 | 
				
			||||||
 | 
					                            mov x, osr
 | 
				
			||||||
 | 
					                            bytes2:
 | 
				
			||||||
 | 
					                                set y, 7
 | 
				
			||||||
 | 
					                                bit2:
 | 
				
			||||||
 | 
					                                    set pindirs, 1 
 | 
				
			||||||
 | 
					                                    set pins, 0 [1]  
 | 
				
			||||||
 | 
					                                    set pindirs, 0 [5]
 | 
				
			||||||
 | 
					                                    in pins,1 [10]   
 | 
				
			||||||
 | 
					                                    jmp y--,bit2
 | 
				
			||||||
 | 
					                            jmp x--,bytes2
 | 
				
			||||||
 | 
					                .wrap
 | 
				
			||||||
 | 
					            "#,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pin = common.make_pio_pin(pin);
 | 
				
			||||||
 | 
					        let mut cfg = Config::default();
 | 
				
			||||||
 | 
					        cfg.use_program(&common.load_program(&prg.program), &[]);
 | 
				
			||||||
 | 
					        cfg.set_out_pins(&[&pin]);
 | 
				
			||||||
 | 
					        cfg.set_in_pins(&[&pin]);
 | 
				
			||||||
 | 
					        cfg.set_set_pins(&[&pin]);
 | 
				
			||||||
 | 
					        cfg.shift_in = ShiftConfig {
 | 
				
			||||||
 | 
					            auto_fill: true,
 | 
				
			||||||
 | 
					            direction: ShiftDirection::Right,
 | 
				
			||||||
 | 
					            threshold: 8,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        cfg.clock_divider = 255_u8.into();
 | 
				
			||||||
 | 
					        sm.set_config(&cfg);
 | 
				
			||||||
 | 
					        sm.set_enable(true);
 | 
				
			||||||
 | 
					        Self { sm }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write bytes over the wire
 | 
				
			||||||
 | 
					    async fn write_bytes(&mut self, bytes: &[u8]) {
 | 
				
			||||||
 | 
					        self.sm.tx().wait_push(250).await;
 | 
				
			||||||
 | 
					        self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
 | 
				
			||||||
 | 
					        for b in bytes {
 | 
				
			||||||
 | 
					            self.sm.tx().wait_push(*b as u32).await;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read bytes from the wire
 | 
				
			||||||
 | 
					    async fn read_bytes(&mut self, bytes: &mut [u8]) {
 | 
				
			||||||
 | 
					        self.sm.tx().wait_push(0).await;
 | 
				
			||||||
 | 
					        self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
 | 
				
			||||||
 | 
					        for b in bytes.iter_mut() {
 | 
				
			||||||
 | 
					            *b = (self.sm.rx().wait_pull().await >> 24) as u8;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Calculate CRC8 of the data
 | 
				
			||||||
 | 
					    fn crc8(data: &[u8]) -> u8 {
 | 
				
			||||||
 | 
					        let mut temp;
 | 
				
			||||||
 | 
					        let mut data_byte;
 | 
				
			||||||
 | 
					        let mut crc = 0;
 | 
				
			||||||
 | 
					        for b in data {
 | 
				
			||||||
 | 
					            data_byte = *b;
 | 
				
			||||||
 | 
					            for _ in 0..8 {
 | 
				
			||||||
 | 
					                temp = (crc ^ data_byte) & 0x01;
 | 
				
			||||||
 | 
					                crc >>= 1;
 | 
				
			||||||
 | 
					                if temp != 0 {
 | 
				
			||||||
 | 
					                    crc ^= 0x8C;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                data_byte >>= 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        crc
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
 | 
				
			||||||
 | 
					    pub async fn start(&mut self) {
 | 
				
			||||||
 | 
					        self.write_bytes(&[0xCC, 0x44]).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
 | 
				
			||||||
 | 
					    pub async fn temperature(&mut self) -> Result<f32, ()> {
 | 
				
			||||||
 | 
					        self.write_bytes(&[0xCC, 0xBE]).await;
 | 
				
			||||||
 | 
					        let mut data = [0; 9];
 | 
				
			||||||
 | 
					        self.read_bytes(&mut data).await;
 | 
				
			||||||
 | 
					        match Self::crc8(&data) == 0 {
 | 
				
			||||||
 | 
					            true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
 | 
				
			||||||
 | 
					            false => Err(()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								examples/rp23/src/bin/trng.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								examples/rp23/src/bin/trng.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					//! This example shows TRNG usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_rp::bind_interrupts;
 | 
				
			||||||
 | 
					use embassy_rp::block::ImageDef;
 | 
				
			||||||
 | 
					use embassy_rp::gpio::{Level, Output};
 | 
				
			||||||
 | 
					use embassy_rp::peripherals::TRNG;
 | 
				
			||||||
 | 
					use embassy_rp::trng::Trng;
 | 
				
			||||||
 | 
					use embassy_time::Timer;
 | 
				
			||||||
 | 
					use rand::RngCore;
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[link_section = ".start_block"]
 | 
				
			||||||
 | 
					#[used]
 | 
				
			||||||
 | 
					pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Program metadata for `picotool info`
 | 
				
			||||||
 | 
					#[link_section = ".bi_entries"]
 | 
				
			||||||
 | 
					#[used]
 | 
				
			||||||
 | 
					pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
 | 
				
			||||||
 | 
					    embassy_rp::binary_info::rp_program_name!(c"example"),
 | 
				
			||||||
 | 
					    embassy_rp::binary_info::rp_cargo_version!(),
 | 
				
			||||||
 | 
					    embassy_rp::binary_info::rp_program_description!(c"Blinky"),
 | 
				
			||||||
 | 
					    embassy_rp::binary_info::rp_program_build_attribute!(),
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bind_interrupts!(struct Irqs {
 | 
				
			||||||
 | 
					    TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    let peripherals = embassy_rp::init(Default::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Initialize the TRNG with default configuration
 | 
				
			||||||
 | 
					    let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());
 | 
				
			||||||
 | 
					    // A buffer to collect random bytes in.
 | 
				
			||||||
 | 
					    let mut randomness = [0u8; 58];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut led = Output::new(peripherals.PIN_25, Level::Low);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        trng.fill_bytes(&mut randomness).await;
 | 
				
			||||||
 | 
					        info!("Random bytes async {}", &randomness);
 | 
				
			||||||
 | 
					        trng.blocking_fill_bytes(&mut randomness);
 | 
				
			||||||
 | 
					        info!("Random bytes blocking {}", &randomness);
 | 
				
			||||||
 | 
					        let random_u32 = trng.next_u32();
 | 
				
			||||||
 | 
					        let random_u64 = trng.next_u64();
 | 
				
			||||||
 | 
					        info!("Random u32 {} u64 {}", random_u32, random_u64);
 | 
				
			||||||
 | 
					        // Random number of blinks between 0 and 31
 | 
				
			||||||
 | 
					        let blinks = random_u32 % 32;
 | 
				
			||||||
 | 
					        for _ in 0..blinks {
 | 
				
			||||||
 | 
					            led.set_high();
 | 
				
			||||||
 | 
					            Timer::after_millis(20).await;
 | 
				
			||||||
 | 
					            led.set_low();
 | 
				
			||||||
 | 
					            Timer::after_millis(20).await;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Timer::after_millis(1000).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								examples/stm32f4/src/bin/eth_compliance_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								examples/stm32f4/src/bin/eth_compliance_test.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::*;
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_stm32::eth::generic_smi::GenericSMI;
 | 
				
			||||||
 | 
					use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement};
 | 
				
			||||||
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
 | 
					use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
 | 
				
			||||||
 | 
					use embassy_time::Timer;
 | 
				
			||||||
 | 
					use static_cell::StaticCell;
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bind_interrupts!(struct Irqs {
 | 
				
			||||||
 | 
					    ETH => eth::InterruptHandler;
 | 
				
			||||||
 | 
					    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) -> ! {
 | 
				
			||||||
 | 
					    let mut config = Config::default();
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        use embassy_stm32::rcc::*;
 | 
				
			||||||
 | 
					        config.rcc.hse = Some(Hse {
 | 
				
			||||||
 | 
					            freq: Hertz(8_000_000),
 | 
				
			||||||
 | 
					            mode: HseMode::Bypass,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        config.rcc.pll_src = PllSource::HSE;
 | 
				
			||||||
 | 
					        config.rcc.pll = Some(Pll {
 | 
				
			||||||
 | 
					            prediv: PllPreDiv::DIV4,
 | 
				
			||||||
 | 
					            mul: PllMul::MUL180,
 | 
				
			||||||
 | 
					            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
 | 
				
			||||||
 | 
					            divq: None,
 | 
				
			||||||
 | 
					            divr: None,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        config.rcc.ahb_pre = AHBPrescaler::DIV1;
 | 
				
			||||||
 | 
					        config.rcc.apb1_pre = APBPrescaler::DIV4;
 | 
				
			||||||
 | 
					        config.rcc.apb2_pre = APBPrescaler::DIV2;
 | 
				
			||||||
 | 
					        config.rcc.sys = Sysclk::PLL1_P;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let p = embassy_stm32::init(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info!("Hello Compliance World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const PHY_ADDR: u8 = 0;
 | 
				
			||||||
 | 
					    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();
 | 
				
			||||||
 | 
					    let mut device = Ethernet::new(
 | 
				
			||||||
 | 
					        PACKETS.init(PacketQueue::<4, 4>::new()),
 | 
				
			||||||
 | 
					        p.ETH,
 | 
				
			||||||
 | 
					        Irqs,
 | 
				
			||||||
 | 
					        p.PA1,
 | 
				
			||||||
 | 
					        p.PA2,
 | 
				
			||||||
 | 
					        p.PC1,
 | 
				
			||||||
 | 
					        p.PA7,
 | 
				
			||||||
 | 
					        p.PC4,
 | 
				
			||||||
 | 
					        p.PC5,
 | 
				
			||||||
 | 
					        p.PG13,
 | 
				
			||||||
 | 
					        p.PB13,
 | 
				
			||||||
 | 
					        p.PG11,
 | 
				
			||||||
 | 
					        GenericSMI::new(PHY_ADDR),
 | 
				
			||||||
 | 
					        mac_addr,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let sm = unsafe { device.station_management() };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Just an example. Exact register settings depend on the specific PHY and test.
 | 
				
			||||||
 | 
					    sm.smi_write(PHY_ADDR, 0, 0x2100);
 | 
				
			||||||
 | 
					    sm.smi_write(PHY_ADDR, 11, 0xA000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // NB: Remember to reset the PHY after testing before starting the networking stack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        Timer::after_secs(1).await;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										140
									
								
								examples/stm32h7rs/src/bin/usb_serial.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								examples/stm32h7rs/src/bin/usb_serial.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,140 @@
 | 
				
			|||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use defmt::{panic, *};
 | 
				
			||||||
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
 | 
					use embassy_futures::join::join;
 | 
				
			||||||
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
 | 
					use embassy_stm32::usb::{Driver, Instance};
 | 
				
			||||||
 | 
					use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
 | 
				
			||||||
 | 
					use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
 | 
				
			||||||
 | 
					use embassy_usb::driver::EndpointError;
 | 
				
			||||||
 | 
					use embassy_usb::Builder;
 | 
				
			||||||
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bind_interrupts!(struct Irqs {
 | 
				
			||||||
 | 
					    OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// If you are trying this and your USB device doesn't connect, the most
 | 
				
			||||||
 | 
					// common issues are the RCC config and vbus_detection
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
 | 
				
			||||||
 | 
					// for more information.
 | 
				
			||||||
 | 
					#[embassy_executor::main]
 | 
				
			||||||
 | 
					async fn main(_spawner: Spawner) {
 | 
				
			||||||
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut config = Config::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        use embassy_stm32::rcc::*;
 | 
				
			||||||
 | 
					        config.rcc.hse = Some(Hse {
 | 
				
			||||||
 | 
					            freq: Hertz(24_000_000),
 | 
				
			||||||
 | 
					            mode: HseMode::Oscillator,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        config.rcc.pll1 = Some(Pll {
 | 
				
			||||||
 | 
					            source: PllSource::HSE,
 | 
				
			||||||
 | 
					            prediv: PllPreDiv::DIV12,
 | 
				
			||||||
 | 
					            mul: PllMul::MUL300,
 | 
				
			||||||
 | 
					            divp: Some(PllDiv::DIV1), //600 MHz
 | 
				
			||||||
 | 
					            divq: Some(PllDiv::DIV2), // 300 MHz
 | 
				
			||||||
 | 
					            divr: Some(PllDiv::DIV2), // 300 MHz
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        config.rcc.sys = Sysclk::PLL1_P; // 600 MHz
 | 
				
			||||||
 | 
					        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz
 | 
				
			||||||
 | 
					        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz
 | 
				
			||||||
 | 
					        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz
 | 
				
			||||||
 | 
					        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz
 | 
				
			||||||
 | 
					        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz
 | 
				
			||||||
 | 
					        config.rcc.voltage_scale = VoltageScale::HIGH;
 | 
				
			||||||
 | 
					        config.rcc.mux.usbphycsel = mux::Usbphycsel::HSE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p = embassy_stm32::init(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create the driver, from the HAL.
 | 
				
			||||||
 | 
					    let mut ep_out_buffer = [0u8; 256];
 | 
				
			||||||
 | 
					    let mut config = embassy_stm32::usb::Config::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Do not enable vbus_detection. This is a safe default that works in all boards.
 | 
				
			||||||
 | 
					    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
 | 
				
			||||||
 | 
					    // to enable vbus_detection to comply with the USB spec. If you enable it, the board
 | 
				
			||||||
 | 
					    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
 | 
				
			||||||
 | 
					    config.vbus_detection = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &mut ep_out_buffer, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create embassy-usb Config
 | 
				
			||||||
 | 
					    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
 | 
				
			||||||
 | 
					    config.manufacturer = Some("Embassy");
 | 
				
			||||||
 | 
					    config.product = Some("USB-serial example");
 | 
				
			||||||
 | 
					    config.serial_number = Some("12345678");
 | 
				
			||||||
 | 
					    // 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 config_descriptor = [0; 256];
 | 
				
			||||||
 | 
					    let mut bos_descriptor = [0; 256];
 | 
				
			||||||
 | 
					    let mut control_buf = [0; 64];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut state = State::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut builder = Builder::new(
 | 
				
			||||||
 | 
					        driver,
 | 
				
			||||||
 | 
					        config,
 | 
				
			||||||
 | 
					        &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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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;
 | 
				
			||||||
 | 
					            info!("Connected");
 | 
				
			||||||
 | 
					            let _ = echo(&mut class).await;
 | 
				
			||||||
 | 
					            info!("Disconnected");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Run everything concurrently.
 | 
				
			||||||
 | 
					    // If we had made everything `'static` above instead, we could do this using separate tasks instead.
 | 
				
			||||||
 | 
					    join(usb_fut, echo_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?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -7,7 +7,8 @@ use common::*;
 | 
				
			|||||||
use defmt::assert_eq;
 | 
					use defmt::assert_eq;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::gpio::{Level, Output, Speed};
 | 
					use embassy_stm32::gpio::{Level, Output, Speed};
 | 
				
			||||||
use embassy_stm32::spi::{self, Spi};
 | 
					use embassy_stm32::mode::Blocking;
 | 
				
			||||||
 | 
					use embassy_stm32::spi::{self, Spi, Word};
 | 
				
			||||||
use embassy_stm32::time::Hertz;
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
@ -31,11 +32,58 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
        spi_config,
 | 
					        spi_config,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE];
 | 
					    test_txrx::<u8>(&mut spi);
 | 
				
			||||||
 | 
					    test_txrx::<u16>(&mut spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Assert the RCC bit gets disabled on drop.
 | 
				
			||||||
 | 
					    #[cfg(feature = "stm32f429zi")]
 | 
				
			||||||
 | 
					    defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en());
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					    #[cfg(feature = "stm32f429zi")]
 | 
				
			||||||
 | 
					    defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // test rx-only configuration
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config);
 | 
				
			||||||
 | 
					    let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test_rx::<u8>(&mut spi, &mut mosi_out);
 | 
				
			||||||
 | 
					    test_rx::<u16>(&mut spi, &mut mosi_out);
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					    drop(mosi_out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config);
 | 
				
			||||||
 | 
					    test_tx::<u8>(&mut spi);
 | 
				
			||||||
 | 
					    test_tx::<u16>(&mut spi);
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config);
 | 
				
			||||||
 | 
					    test_tx::<u8>(&mut spi);
 | 
				
			||||||
 | 
					    test_tx::<u16>(&mut spi);
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info!("Test OK");
 | 
				
			||||||
 | 
					    cortex_m::asm::bkpt();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>)
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let data: [W; 9] = [
 | 
				
			||||||
 | 
					        0x00u8.into(),
 | 
				
			||||||
 | 
					        0xFFu8.into(),
 | 
				
			||||||
 | 
					        0xAAu8.into(),
 | 
				
			||||||
 | 
					        0x55u8.into(),
 | 
				
			||||||
 | 
					        0xC0u8.into(),
 | 
				
			||||||
 | 
					        0xFFu8.into(),
 | 
				
			||||||
 | 
					        0xEEu8.into(),
 | 
				
			||||||
 | 
					        0xC0u8.into(),
 | 
				
			||||||
 | 
					        0xDEu8.into(),
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.
 | 
					    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.
 | 
				
			||||||
    // so we should get the data we sent back.
 | 
					    // so we should get the data we sent back.
 | 
				
			||||||
    let mut buf = [0; 9];
 | 
					    let mut buf = [W::default(); 9];
 | 
				
			||||||
    spi.blocking_transfer(&mut buf, &data).unwrap();
 | 
					    spi.blocking_transfer(&mut buf, &data).unwrap();
 | 
				
			||||||
    assert_eq!(buf, data);
 | 
					    assert_eq!(buf, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -59,47 +107,33 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();
 | 
					    spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();
 | 
				
			||||||
    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
					    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
				
			||||||
    spi.blocking_write::<u8>(&[]).unwrap();
 | 
					    spi.blocking_write::<u8>(&[]).unwrap();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assert the RCC bit gets disabled on drop.
 | 
					fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>, mosi_out: &mut Output<'_>)
 | 
				
			||||||
    #[cfg(feature = "stm32f429zi")]
 | 
					where
 | 
				
			||||||
    defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en());
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
    drop(spi);
 | 
					{
 | 
				
			||||||
    #[cfg(feature = "stm32f429zi")]
 | 
					    let mut buf = [W::default(); 9];
 | 
				
			||||||
    defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // test rx-only configuration
 | 
					 | 
				
			||||||
    let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config);
 | 
					 | 
				
			||||||
    let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh);
 | 
					 | 
				
			||||||
    mosi_out.set_high();
 | 
					    mosi_out.set_high();
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    mosi_out.set_low();
 | 
					    mosi_out.set_low();
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0x00; 9]);
 | 
					    assert_eq!(buf, [W::default(); 9]);
 | 
				
			||||||
    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
					    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
				
			||||||
    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
					    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
				
			||||||
    drop(mosi_out);
 | 
					}
 | 
				
			||||||
    drop(spi);
 | 
					
 | 
				
			||||||
 | 
					fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>)
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let buf = [W::default(); 9];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.
 | 
					    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.
 | 
				
			||||||
    let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config);
 | 
					 | 
				
			||||||
    spi.blocking_transfer(&mut buf, &data).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_transfer_in_place(&mut buf).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_transfer::<u8>(&mut [], &[]).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_write::<u8>(&[]).unwrap();
 | 
					 | 
				
			||||||
    drop(spi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Test tx-only nosck.
 | 
					 | 
				
			||||||
    let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config);
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					    spi.blocking_write(&buf).unwrap();
 | 
				
			||||||
    spi.blocking_write::<u8>(&[]).unwrap();
 | 
					    spi.blocking_write::<u8>(&[]).unwrap();
 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					    spi.blocking_write(&buf).unwrap();
 | 
				
			||||||
    drop(spi);
 | 
					    spi.blocking_write::<u8>(&[]).unwrap();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    info!("Test OK");
 | 
					 | 
				
			||||||
    cortex_m::asm::bkpt();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,8 @@ use common::*;
 | 
				
			|||||||
use defmt::assert_eq;
 | 
					use defmt::assert_eq;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::gpio::{Level, Output, Speed};
 | 
					use embassy_stm32::gpio::{Level, Output, Speed};
 | 
				
			||||||
use embassy_stm32::spi::{self, Spi};
 | 
					use embassy_stm32::mode::Async;
 | 
				
			||||||
 | 
					use embassy_stm32::spi::{self, Spi, Word};
 | 
				
			||||||
use embassy_stm32::time::Hertz;
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
@ -35,11 +36,61 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
        spi_config,
 | 
					        spi_config,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE];
 | 
					    test_txrx::<u8>(&mut spi).await;
 | 
				
			||||||
 | 
					    test_txrx::<u16>(&mut spi).await;
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // test rx-only configuration
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_rxonly(
 | 
				
			||||||
 | 
					        &mut spi_peri,
 | 
				
			||||||
 | 
					        &mut sck,
 | 
				
			||||||
 | 
					        &mut miso,
 | 
				
			||||||
 | 
					        // SPIv1/f1 requires txdma even if rxonly.
 | 
				
			||||||
 | 
					        #[cfg(not(feature = "spi-v345"))]
 | 
				
			||||||
 | 
					        &mut tx_dma,
 | 
				
			||||||
 | 
					        &mut rx_dma,
 | 
				
			||||||
 | 
					        spi_config,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test_rx::<u8>(&mut spi, &mut mosi_out).await;
 | 
				
			||||||
 | 
					    test_rx::<u16>(&mut spi, &mut mosi_out).await;
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					    drop(mosi_out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config);
 | 
				
			||||||
 | 
					    test_tx::<u8>(&mut spi).await;
 | 
				
			||||||
 | 
					    test_tx::<u16>(&mut spi).await;
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config);
 | 
				
			||||||
 | 
					    test_tx::<u8>(&mut spi).await;
 | 
				
			||||||
 | 
					    test_tx::<u16>(&mut spi).await;
 | 
				
			||||||
 | 
					    drop(spi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info!("Test OK");
 | 
				
			||||||
 | 
					    cortex_m::asm::bkpt();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>)
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let data: [W; 9] = [
 | 
				
			||||||
 | 
					        0x00u8.into(),
 | 
				
			||||||
 | 
					        0xFFu8.into(),
 | 
				
			||||||
 | 
					        0xAAu8.into(),
 | 
				
			||||||
 | 
					        0x55u8.into(),
 | 
				
			||||||
 | 
					        0xC0u8.into(),
 | 
				
			||||||
 | 
					        0xFFu8.into(),
 | 
				
			||||||
 | 
					        0xEEu8.into(),
 | 
				
			||||||
 | 
					        0xC0u8.into(),
 | 
				
			||||||
 | 
					        0xDEu8.into(),
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.
 | 
					    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.
 | 
				
			||||||
    // so we should get the data we sent back.
 | 
					    // so we should get the data we sent back.
 | 
				
			||||||
    let mut buf = [0; 9];
 | 
					    let mut buf = [W::default(); 9];
 | 
				
			||||||
    spi.transfer(&mut buf, &data).await.unwrap();
 | 
					    spi.transfer(&mut buf, &data).await.unwrap();
 | 
				
			||||||
    assert_eq!(buf, data);
 | 
					    assert_eq!(buf, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,44 +134,41 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    spi.blocking_write(&buf).unwrap();
 | 
					    spi.blocking_write(&buf).unwrap();
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    spi.write(&buf).await.unwrap();
 | 
					    spi.write(&buf).await.unwrap();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    core::mem::drop(spi);
 | 
					async fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>, mosi_out: &mut Output<'_>)
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let mut buf = [W::default(); 9];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // test rx-only configuration
 | 
					 | 
				
			||||||
    let mut spi = Spi::new_rxonly(
 | 
					 | 
				
			||||||
        &mut spi_peri,
 | 
					 | 
				
			||||||
        &mut sck,
 | 
					 | 
				
			||||||
        &mut miso,
 | 
					 | 
				
			||||||
        // SPIv1/f1 requires txdma even if rxonly.
 | 
					 | 
				
			||||||
        #[cfg(not(feature = "spi-v345"))]
 | 
					 | 
				
			||||||
        &mut tx_dma,
 | 
					 | 
				
			||||||
        &mut rx_dma,
 | 
					 | 
				
			||||||
        spi_config,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh);
 | 
					 | 
				
			||||||
    mosi_out.set_high();
 | 
					    mosi_out.set_high();
 | 
				
			||||||
    spi.read(&mut buf).await.unwrap();
 | 
					    spi.read(&mut buf).await.unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    spi.read(&mut buf).await.unwrap();
 | 
					    spi.read(&mut buf).await.unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    spi.read(&mut buf).await.unwrap();
 | 
					    spi.read(&mut buf).await.unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    spi.blocking_read(&mut buf).unwrap();
 | 
					    spi.blocking_read(&mut buf).unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0xff; 9]);
 | 
					    assert_eq!(buf, [!W::default(); 9]);
 | 
				
			||||||
    mosi_out.set_low();
 | 
					    mosi_out.set_low();
 | 
				
			||||||
    spi.read(&mut buf).await.unwrap();
 | 
					    spi.read(&mut buf).await.unwrap();
 | 
				
			||||||
    assert_eq!(buf, [0x00; 9]);
 | 
					    assert_eq!(buf, [W::default(); 9]);
 | 
				
			||||||
    spi.read::<u8>(&mut []).await.unwrap();
 | 
					    spi.read::<u8>(&mut []).await.unwrap();
 | 
				
			||||||
    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
					    spi.blocking_read::<u8>(&mut []).unwrap();
 | 
				
			||||||
    drop(mosi_out);
 | 
					}
 | 
				
			||||||
    drop(spi);
 | 
					
 | 
				
			||||||
 | 
					async fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>)
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: core::ops::Not<Output = W>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let buf = [W::default(); 9];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.
 | 
					    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.
 | 
				
			||||||
    let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config);
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					    spi.blocking_write(&buf).unwrap();
 | 
				
			||||||
    spi.write(&buf).await.unwrap();
 | 
					    spi.write(&buf).await.unwrap();
 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					    spi.blocking_write(&buf).unwrap();
 | 
				
			||||||
@ -129,20 +177,4 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    spi.write(&buf).await.unwrap();
 | 
					    spi.write(&buf).await.unwrap();
 | 
				
			||||||
    spi.write::<u8>(&[]).await.unwrap();
 | 
					    spi.write::<u8>(&[]).await.unwrap();
 | 
				
			||||||
    spi.blocking_write::<u8>(&[]).unwrap();
 | 
					    spi.blocking_write::<u8>(&[]).unwrap();
 | 
				
			||||||
    drop(spi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Test tx-only nosck.
 | 
					 | 
				
			||||||
    let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config);
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					 | 
				
			||||||
    spi.write(&buf).await.unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_write(&buf).unwrap();
 | 
					 | 
				
			||||||
    spi.write(&buf).await.unwrap();
 | 
					 | 
				
			||||||
    spi.write(&buf).await.unwrap();
 | 
					 | 
				
			||||||
    spi.write::<u8>(&[]).await.unwrap();
 | 
					 | 
				
			||||||
    spi.blocking_write::<u8>(&[]).unwrap();
 | 
					 | 
				
			||||||
    drop(spi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    info!("Test OK");
 | 
					 | 
				
			||||||
    cortex_m::asm::bkpt();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user