Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim
This commit is contained in:
commit
8141d53d94
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -6,16 +6,16 @@
|
|||||||
"rust-analyzer.check.allTargets": false,
|
"rust-analyzer.check.allTargets": false,
|
||||||
"rust-analyzer.check.noDefaultFeatures": true,
|
"rust-analyzer.check.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.noDefaultFeatures": true,
|
"rust-analyzer.cargo.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
||||||
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
||||||
"rust-analyzer.cargo.features": [
|
"rust-analyzer.cargo.features": [
|
||||||
"nightly",
|
///"nightly",
|
||||||
],
|
],
|
||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
// Declare for the target you wish to develop
|
// Declare for the target you wish to develop
|
||||||
// "embassy-executor/Cargo.toml",
|
// "embassy-executor/Cargo.toml",
|
||||||
// "embassy-sync/Cargo.toml",
|
// "embassy-sync/Cargo.toml",
|
||||||
"examples/nrf52840/Cargo.toml",
|
"examples/stm32wl/Cargo.toml",
|
||||||
// "examples/nrf5340/Cargo.toml",
|
// "examples/nrf5340/Cargo.toml",
|
||||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||||
// "examples/rp/Cargo.toml",
|
// "examples/rp/Cargo.toml",
|
||||||
|
@ -99,10 +99,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
|
|||||||
|
|
||||||
### Running examples
|
### Running examples
|
||||||
|
|
||||||
- Install `probe-rs-cli` with defmt support.
|
- Install `probe-rs`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install probe-rs-cli
|
cargo install probe-rs --features cli
|
||||||
```
|
```
|
||||||
|
|
||||||
- Change directory to the sample's base directory. For example:
|
- Change directory to the sample's base directory. For example:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# cyw43
|
# cyw43
|
||||||
|
|
||||||
WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
|
Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
@ -19,18 +19,18 @@ Working:
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
- Setting a custom MAC address.
|
- Setting a custom MAC address.
|
||||||
- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?)
|
- Bus sleep (for power consumption optimization)
|
||||||
|
|
||||||
## Running the examples
|
## Running the examples
|
||||||
|
|
||||||
- `cargo install probe-rs-cli`
|
- `cargo install probe-rs --features cli`
|
||||||
- `cd examples/rpi-pico-w`
|
- `cd examples/rp`
|
||||||
### Example 1: Scan the wifi stations
|
### Example 1: Scan the wifi stations
|
||||||
- `cargo run --release --bin wifi_scan`
|
- `cargo run --release --bin wifi_scan`
|
||||||
### Example 2: Create an access point (IP and credentials in the code)
|
### Example 2: Create an access point (IP and credentials in the code)
|
||||||
- `cargo run --release --bin tcp_server_ap`
|
- `cargo run --release --bin wifi_ap_tcp_server`
|
||||||
### Example 3: Connect to an existing network and create a server
|
### Example 3: Connect to an existing network and create a server
|
||||||
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release`
|
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server`
|
||||||
|
|
||||||
After a few seconds, you should see that DHCP picks up an IP address like this
|
After a few seconds, you should see that DHCP picks up an IP address like this
|
||||||
```
|
```
|
||||||
|
@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Load applications with our without the softdevice.
|
* Load applications with or without the softdevice.
|
||||||
* Configure bootloader partitions based on linker script.
|
* Configure bootloader partitions based on linker script.
|
||||||
* Using watchdog timer to detect application failure.
|
* Using watchdog timer to detect application failure.
|
||||||
|
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
|
||||||
@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
|
|||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
96
embassy-net-driver-channel/README.md
Normal file
96
embassy-net-driver-channel/README.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# embassy-net-driver-channel
|
||||||
|
|
||||||
|
This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
|
||||||
|
higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
|
||||||
|
|
||||||
|
The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
|
||||||
|
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
||||||
|
knows when to poll your driver again to make more progress.
|
||||||
|
|
||||||
|
With `embassy-net-driver-channel`
|
||||||
|
|
||||||
|
## A note about deadlocks
|
||||||
|
|
||||||
|
When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
loop {
|
||||||
|
// Wait for either..
|
||||||
|
match select(
|
||||||
|
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
||||||
|
irq_pin.wait_for_low(),
|
||||||
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
|
tx_chan.tx_buf(),
|
||||||
|
).await {
|
||||||
|
Either::First(_) => {
|
||||||
|
// a packet is ready to be received!
|
||||||
|
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
||||||
|
let n = receive_packet_over_spi(buf).await;
|
||||||
|
rx_chan.rx_done(n);
|
||||||
|
}
|
||||||
|
Either::Second(buf) => {
|
||||||
|
// a packet is ready to be sent!
|
||||||
|
send_packet_over_spi(buf).await;
|
||||||
|
tx_chan.tx_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
|
||||||
|
|
||||||
|
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
||||||
|
|
||||||
|
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
loop {
|
||||||
|
// Wait for either..
|
||||||
|
match select(
|
||||||
|
async {
|
||||||
|
// ... the chip signaling an interrupt, indicating a packet is available to receive
|
||||||
|
irq_pin.wait_for_low().await;
|
||||||
|
// *AND* the buffer is ready...
|
||||||
|
rx_chan.rx_buf().await
|
||||||
|
},
|
||||||
|
// ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
|
tx_chan.tx_buf(),
|
||||||
|
).await {
|
||||||
|
Either::First(buf) => {
|
||||||
|
// a packet is ready to be received!
|
||||||
|
let n = receive_packet_over_spi(buf).await;
|
||||||
|
rx_chan.rx_done(n);
|
||||||
|
}
|
||||||
|
Either::Second(buf) => {
|
||||||
|
// a packet is ready to be sent!
|
||||||
|
send_packet_over_spi(buf).await;
|
||||||
|
tx_chan.tx_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
|
||||||
|
|
||||||
|
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
|
||||||
|
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
|
||||||
|
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
|
||||||
|
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
||||||
|
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
@ -1,4 +1,5 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
// must go first!
|
// must go first!
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
@ -3,7 +3,13 @@ name = "embassy-net-driver"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
|
||||||
@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
|
|||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
@ -1,5 +1,21 @@
|
|||||||
# embassy-net-driver
|
# embassy-net-driver
|
||||||
|
|
||||||
|
This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
|
||||||
|
for a new hardware platform.
|
||||||
|
|
||||||
|
If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
|
||||||
|
|
||||||
|
If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
|
||||||
|
This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
|
||||||
|
if the driver trait has not had breaking changes.
|
||||||
|
|
||||||
|
See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
|
||||||
|
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
|
||||||
|
packet queues for RX and TX.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
|
|
||||||
mod device;
|
mod device;
|
||||||
mod socket;
|
mod socket;
|
||||||
mod spi;
|
mod spi;
|
||||||
@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net).
|
/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
|
||||||
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
|
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
|
||||||
mac_addr: [u8; 6],
|
mac_addr: [u8; 6],
|
||||||
state: &'a mut State<N_RX, N_TX>,
|
state: &'a mut State<N_RX, N_TX>,
|
||||||
|
@ -3,7 +3,13 @@ name = "embassy-net"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Async TCP/IP network stack for embedded systems"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
|
||||||
@ -44,7 +50,6 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
|
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embedded-io = { version = "0.4.0", optional = true }
|
embedded-io = { version = "0.4.0", optional = true }
|
||||||
|
@ -1,30 +1,56 @@
|
|||||||
# embassy-net
|
# embassy-net
|
||||||
|
|
||||||
embassy-net contains an async network API based on smoltcp and embassy, designed
|
`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
|
||||||
for embedded systems.
|
|
||||||
|
|
||||||
## Running the example
|
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
|
||||||
|
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
|
||||||
|
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
|
||||||
|
|
||||||
First, create the tap0 interface. You only need to do this once.
|
## Features
|
||||||
|
|
||||||
```sh
|
- IPv4, IPv6
|
||||||
sudo ip tuntap add name tap0 mode tap user $USER
|
- Ethernet and bare-IP mediums.
|
||||||
sudo ip link set tap0 up
|
- TCP, UDP, DNS, DHCPv4, IGMPv4
|
||||||
sudo ip addr add 192.168.69.100/24 dev tap0
|
- TCP sockets implement the `embedded-io` async traits.
|
||||||
sudo ip -6 addr add fe80::100/64 dev tap0
|
|
||||||
sudo ip -6 addr add fdaa::100/64 dev tap0
|
|
||||||
sudo ip -6 route add fe80::/64 dev tap0
|
|
||||||
sudo ip -6 route add fdaa::/64 dev tap0
|
|
||||||
```
|
|
||||||
|
|
||||||
Second, have something listening there. For example `nc -l 8000`
|
See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
|
||||||
|
unimplemented features of the network protocols.
|
||||||
|
|
||||||
Then run the example located in the `examples` folder:
|
## Hardware support
|
||||||
|
|
||||||
```sh
|
- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
|
||||||
cd $EMBASSY_ROOT/examples/std/
|
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
|
||||||
cargo run --bin net -- --static-ip
|
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
|
||||||
```
|
- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
|
||||||
|
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
|
||||||
|
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
|
||||||
|
- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
|
||||||
|
- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
|
||||||
|
|
||||||
|
## Adding support for new hardware
|
||||||
|
|
||||||
|
To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
|
||||||
|
an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
|
||||||
|
traits.
|
||||||
|
|
||||||
|
Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
|
||||||
|
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
|
||||||
|
packet queues for RX and TX.
|
||||||
|
|
||||||
|
Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
|
||||||
|
This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
|
||||||
|
trait has not had breaking changes.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must
|
||||||
|
link an `embassy-time` driver in your project to use this crate.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ pub struct StackResources<const SOCK: usize> {
|
|||||||
|
|
||||||
impl<const SOCK: usize> StackResources<SOCK> {
|
impl<const SOCK: usize> StackResources<SOCK> {
|
||||||
/// Create a new set of stack resources.
|
/// Create a new set of stack resources.
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
const INIT: Option<dns::DnsQuery> = None;
|
const INIT: Option<dns::DnsQuery> = None;
|
||||||
Self {
|
Self {
|
||||||
@ -419,7 +419,29 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
#[must_use = "to delay the drop handler invocation to the end of the scope"]
|
||||||
|
struct OnDrop<F: FnOnce()> {
|
||||||
|
f: core::mem::MaybeUninit<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> OnDrop<F> {
|
||||||
|
fn new(f: F) -> Self {
|
||||||
|
Self {
|
||||||
|
f: core::mem::MaybeUninit::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn defuse(self) {
|
||||||
|
core::mem::forget(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { self.f.as_ptr().read()() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let drop = OnDrop::new(|| {
|
let drop = OnDrop::new(|| {
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
||||||
|
@ -410,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
warn!(
|
warn!(
|
||||||
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
#[cfg(feature = "reset-pin-as-gpio")]
|
#[cfg(feature = "reset-pin-as-gpio")]
|
||||||
warn!(
|
warn!(
|
||||||
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
warn!(
|
warn!(
|
||||||
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
|
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,11 @@ impl Task {
|
|||||||
Self(ptr)
|
Self(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Triggers this task.
|
||||||
|
pub fn trigger(&mut self) {
|
||||||
|
unsafe { self.0.as_ptr().write_volatile(1) };
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
||||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
||||||
}
|
}
|
||||||
@ -173,6 +178,16 @@ impl Event {
|
|||||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describes whether this Event is currently in a triggered state.
|
||||||
|
pub fn is_triggered(&self) -> bool {
|
||||||
|
unsafe { self.0.as_ptr().read_volatile() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the current register's triggered state, reverting it to 0.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
unsafe { self.0.as_ptr().write_volatile(0) };
|
||||||
|
}
|
||||||
|
|
||||||
/// Address of publish register for this event.
|
/// Address of publish register for this event.
|
||||||
#[cfg(feature = "_dppi")]
|
#[cfg(feature = "_dppi")]
|
||||||
pub fn publish_reg(&self) -> *mut u32 {
|
pub fn publish_reg(&self) -> *mut u32 {
|
||||||
|
@ -76,7 +76,7 @@ embedded-storage = { version = "0.3" }
|
|||||||
rand_core = "0.6.4"
|
rand_core = "0.6.4"
|
||||||
fixed = "1.23.1"
|
fixed = "1.23.1"
|
||||||
|
|
||||||
rp-pac = { version = "5" }
|
rp-pac = { version = "6" }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
|
||||||
|
@ -46,13 +46,13 @@ static CLOCKS: Clocks = Clocks {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum PeriClkSrc {
|
pub enum PeriClkSrc {
|
||||||
Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0,
|
Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _,
|
||||||
PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0,
|
PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _,
|
||||||
PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0,
|
PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0,
|
Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _,
|
||||||
Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0,
|
Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _,
|
||||||
// Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0,
|
// Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
|
||||||
// Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0,
|
// Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -251,12 +251,12 @@ pub struct SysClkConfig {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum UsbClkSrc {
|
pub enum UsbClkSrc {
|
||||||
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0,
|
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0,
|
PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _,
|
||||||
Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0,
|
Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _,
|
||||||
Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0,
|
Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _,
|
||||||
// Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0,
|
// Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
|
||||||
// Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0,
|
// Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UsbClkConfig {
|
pub struct UsbClkConfig {
|
||||||
@ -269,12 +269,12 @@ pub struct UsbClkConfig {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum AdcClkSrc {
|
pub enum AdcClkSrc {
|
||||||
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0,
|
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0,
|
PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
|
||||||
Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0,
|
Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
|
||||||
Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0,
|
Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _,
|
||||||
// Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0,
|
// Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
|
||||||
// Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0,
|
// Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AdcClkConfig {
|
pub struct AdcClkConfig {
|
||||||
@ -287,12 +287,12 @@ pub struct AdcClkConfig {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum RtcClkSrc {
|
pub enum RtcClkSrc {
|
||||||
PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0,
|
PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0,
|
PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
|
||||||
Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0,
|
Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
|
||||||
Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0,
|
Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _,
|
||||||
// Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0,
|
// Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
|
||||||
// Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0,
|
// Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RtcClkConfig {
|
pub struct RtcClkConfig {
|
||||||
@ -396,7 +396,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
w.set_src(ref_src);
|
w.set_src(ref_src);
|
||||||
w.set_auxsrc(ref_aux);
|
w.set_auxsrc(ref_aux);
|
||||||
});
|
});
|
||||||
while c.clk_ref_selected().read() != 1 << ref_src.0 {}
|
while c.clk_ref_selected().read() != 1 << ref_src as u32 {}
|
||||||
c.clk_ref_div().write(|w| {
|
c.clk_ref_div().write(|w| {
|
||||||
w.set_int(config.ref_clk.div);
|
w.set_int(config.ref_clk.div);
|
||||||
});
|
});
|
||||||
@ -425,13 +425,13 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
|
CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
|
||||||
if sys_src != ClkSysCtrlSrc::CLK_REF {
|
if sys_src != ClkSysCtrlSrc::CLK_REF {
|
||||||
c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
|
c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
|
||||||
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {}
|
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {}
|
||||||
}
|
}
|
||||||
c.clk_sys_ctrl().write(|w| {
|
c.clk_sys_ctrl().write(|w| {
|
||||||
w.set_auxsrc(sys_aux);
|
w.set_auxsrc(sys_aux);
|
||||||
w.set_src(sys_src);
|
w.set_src(sys_src);
|
||||||
});
|
});
|
||||||
while c.clk_sys_selected().read() != 1 << sys_src.0 {}
|
while c.clk_sys_selected().read() != 1 << sys_src as u32 {}
|
||||||
c.clk_sys_div().write(|w| {
|
c.clk_sys_div().write(|w| {
|
||||||
w.set_int(config.sys_clk.div_int);
|
w.set_int(config.sys_clk.div_int);
|
||||||
w.set_frac(config.sys_clk.div_frac);
|
w.set_frac(config.sys_clk.div_frac);
|
||||||
@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
if let Some(src) = config.peri_clk_src {
|
if let Some(src) = config.peri_clk_src {
|
||||||
c.clk_peri_ctrl().write(|w| {
|
c.clk_peri_ctrl().write(|w| {
|
||||||
w.set_enable(true);
|
w.set_enable(true);
|
||||||
w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _));
|
w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _));
|
||||||
});
|
});
|
||||||
let peri_freq = match src {
|
let peri_freq = match src {
|
||||||
PeriClkSrc::Sys => clk_sys_freq,
|
PeriClkSrc::Sys => clk_sys_freq,
|
||||||
@ -468,7 +468,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
c.clk_usb_ctrl().write(|w| {
|
c.clk_usb_ctrl().write(|w| {
|
||||||
w.set_phase(conf.phase);
|
w.set_phase(conf.phase);
|
||||||
w.set_enable(true);
|
w.set_enable(true);
|
||||||
w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _));
|
w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
|
||||||
});
|
});
|
||||||
let usb_freq = match conf.src {
|
let usb_freq = match conf.src {
|
||||||
UsbClkSrc::PllUsb => pll_usb_freq,
|
UsbClkSrc::PllUsb => pll_usb_freq,
|
||||||
@ -491,7 +491,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
c.clk_adc_ctrl().write(|w| {
|
c.clk_adc_ctrl().write(|w| {
|
||||||
w.set_phase(conf.phase);
|
w.set_phase(conf.phase);
|
||||||
w.set_enable(true);
|
w.set_enable(true);
|
||||||
w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _));
|
w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
|
||||||
});
|
});
|
||||||
let adc_in_freq = match conf.src {
|
let adc_in_freq = match conf.src {
|
||||||
AdcClkSrc::PllUsb => pll_usb_freq,
|
AdcClkSrc::PllUsb => pll_usb_freq,
|
||||||
@ -517,7 +517,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
c.clk_rtc_ctrl().write(|w| {
|
c.clk_rtc_ctrl().write(|w| {
|
||||||
w.set_phase(conf.phase);
|
w.set_phase(conf.phase);
|
||||||
w.set_enable(true);
|
w.set_enable(true);
|
||||||
w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _));
|
w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
|
||||||
});
|
});
|
||||||
let rtc_in_freq = match conf.src {
|
let rtc_in_freq = match conf.src {
|
||||||
RtcClkSrc::PllUsb => pll_usb_freq,
|
RtcClkSrc::PllUsb => pll_usb_freq,
|
||||||
@ -718,7 +718,7 @@ impl<'d, T: Pin> Drop for Gpin<'d, T> {
|
|||||||
self.gpin
|
self.gpin
|
||||||
.io()
|
.io()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
|
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,17 +743,17 @@ impl_gpoutpin!(PIN_25, 3);
|
|||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum GpoutSrc {
|
pub enum GpoutSrc {
|
||||||
PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0,
|
PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _,
|
||||||
// Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0,
|
// Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
|
||||||
// Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0,
|
// Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||||
PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0,
|
PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0,
|
Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _,
|
||||||
Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0,
|
Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _,
|
||||||
Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0,
|
Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _,
|
||||||
Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0,
|
Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
|
||||||
Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0,
|
Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
|
||||||
Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0,
|
Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
|
||||||
Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0,
|
Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gpout<'d, T: GpoutPin> {
|
pub struct Gpout<'d, T: GpoutPin> {
|
||||||
@ -780,7 +780,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
|
|||||||
pub fn set_src(&self, src: GpoutSrc) {
|
pub fn set_src(&self, src: GpoutSrc) {
|
||||||
let c = pac::CLOCKS;
|
let c = pac::CLOCKS;
|
||||||
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
|
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
|
||||||
w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _));
|
w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,7 +831,7 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
|
|||||||
self.gpout
|
self.gpout
|
||||||
.io()
|
.io()
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
|
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
pin.io().ctrl().write(|w| {
|
pin.io().ctrl().write(|w| {
|
||||||
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0);
|
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
|
||||||
});
|
});
|
||||||
|
|
||||||
Self { pin }
|
Self { pin }
|
||||||
@ -618,7 +618,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.pin.pad_ctrl().write(|_| {});
|
self.pin.pad_ctrl().write(|_| {});
|
||||||
self.pin.io().ctrl().write(|w| {
|
self.pin.io().ctrl().write(|w| {
|
||||||
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0);
|
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,7 +834,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
|
|||||||
/// of [`Pio`] do not keep pin registrations alive.**
|
/// of [`Pio`] do not keep pin registrations alive.**
|
||||||
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.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0));
|
pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
|
||||||
// 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 {
|
||||||
@ -998,7 +998,7 @@ fn on_pio_drop<PIO: Instance>() {
|
|||||||
let state = PIO::state();
|
let state = PIO::state();
|
||||||
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 = Gpio0ctrlFuncsel::NULL.0;
|
let null = Gpio0ctrlFuncsel::NULL as _;
|
||||||
// we only have 30 pins. don't test the other two since gpio() asserts.
|
// we only have 30 pins. don't test the other two since gpio() asserts.
|
||||||
for i in 0..30 {
|
for i in 0..30 {
|
||||||
if used_pins & (1 << i) != 0 {
|
if used_pins & (1 << i) != 0 {
|
||||||
|
@ -353,6 +353,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| {
|
||||||
BUS_WAKER.register(cx.waker());
|
BUS_WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
// TODO: implement VBUS detection.
|
||||||
if !self.inited {
|
if !self.inited {
|
||||||
self.inited = true;
|
self.inited = true;
|
||||||
return Poll::Ready(Event::PowerDetected);
|
return Poll::Ready(Event::PowerDetected);
|
||||||
|
@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
stm32-metapac = { version = "10", default-features = false, features = ["metadata"]}
|
stm32-metapac = { version = "12", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
|
@ -709,6 +709,8 @@ fn main() {
|
|||||||
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
||||||
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
||||||
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
||||||
|
(("dac", "CH1"), quote!(crate::dac::DmaCh1)),
|
||||||
|
(("dac", "CH2"), quote!(crate::dac::DmaCh2)),
|
||||||
]
|
]
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
@ -211,10 +211,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
#[cfg(not(stm32g0))]
|
#[cfg(not(stm32g0))]
|
||||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||||
let sample_time = sample_time.into();
|
let sample_time = sample_time.into();
|
||||||
if ch <= 9 {
|
T::regs()
|
||||||
T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
|
.smpr(ch as usize / 10)
|
||||||
} else {
|
.modify(|reg| reg.set_smp(ch as usize % 10, sample_time));
|
||||||
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,10 +116,10 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
T::regs().ier().write(|w| {
|
T::regs().ier().write(|w| {
|
||||||
// TODO: fix metapac
|
// TODO: fix metapac
|
||||||
|
|
||||||
w.set_errie(Errie(1));
|
w.set_errie(Errie::from_bits(1));
|
||||||
w.set_fmpie(0, Fmpie(1));
|
w.set_fmpie(0, Fmpie::from_bits(1));
|
||||||
w.set_fmpie(1, Fmpie(1));
|
w.set_fmpie(1, Fmpie::from_bits(1));
|
||||||
w.set_tmeie(Tmeie(1));
|
w.set_tmeie(Tmeie::from_bits(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
T::regs().mcr().write(|w| {
|
T::regs().mcr().write(|w| {
|
||||||
|
@ -1,260 +0,0 @@
|
|||||||
#![macro_use]
|
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
|
||||||
|
|
||||||
use crate::pac::dac;
|
|
||||||
use crate::rcc::RccPeripheral;
|
|
||||||
use crate::{peripherals, Peripheral};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Error {
|
|
||||||
UnconfiguredChannel,
|
|
||||||
InvalidValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Channel {
|
|
||||||
Ch1,
|
|
||||||
Ch2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
fn index(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Channel::Ch1 => 0,
|
|
||||||
Channel::Ch2 => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Ch1Trigger {
|
|
||||||
Tim6,
|
|
||||||
Tim3,
|
|
||||||
Tim7,
|
|
||||||
Tim15,
|
|
||||||
Tim2,
|
|
||||||
Exti9,
|
|
||||||
Software,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ch1Trigger {
|
|
||||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
|
||||||
match self {
|
|
||||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
|
||||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
|
||||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
|
||||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
|
||||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
|
||||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
|
||||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Ch2Trigger {
|
|
||||||
Tim6,
|
|
||||||
Tim8,
|
|
||||||
Tim7,
|
|
||||||
Tim5,
|
|
||||||
Tim2,
|
|
||||||
Tim4,
|
|
||||||
Exti9,
|
|
||||||
Software,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ch2Trigger {
|
|
||||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
|
||||||
match self {
|
|
||||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
|
||||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
|
||||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
|
||||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
|
||||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
|
||||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
|
||||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
|
||||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Alignment {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Value {
|
|
||||||
Bit8(u8),
|
|
||||||
Bit12(u16, Alignment),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Dac<'d, T: Instance> {
|
|
||||||
channels: u8,
|
|
||||||
_peri: PeripheralRef<'d, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> Dac<'d, T> {
|
|
||||||
pub fn new_1ch(peri: impl Peripheral<P = T> + 'd, _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd) -> Self {
|
|
||||||
into_ref!(peri);
|
|
||||||
Self::new_inner(peri, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_2ch(
|
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
|
||||||
_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
|
||||||
_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
|
||||||
) -> Self {
|
|
||||||
into_ref!(peri);
|
|
||||||
Self::new_inner(peri, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self {
|
|
||||||
T::enable();
|
|
||||||
T::reset();
|
|
||||||
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
for ch in 0..channels {
|
|
||||||
reg.set_en(ch as usize, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self { channels, _peri: peri }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check the channel is configured
|
|
||||||
fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> {
|
|
||||||
if ch == Channel::Ch2 && self.channels < 2 {
|
|
||||||
Err(Error::UnconfiguredChannel)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> {
|
|
||||||
self.check_channel_exists(ch)?;
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_en(ch.index(), on);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> {
|
|
||||||
self.set_channel_enable(ch, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> {
|
|
||||||
self.set_channel_enable(ch, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
|
||||||
self.check_channel_exists(Channel::Ch1)?;
|
|
||||||
unwrap!(self.disable_channel(Channel::Ch1));
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_tsel1(trigger.tsel());
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
|
||||||
self.check_channel_exists(Channel::Ch2)?;
|
|
||||||
unwrap!(self.disable_channel(Channel::Ch2));
|
|
||||||
T::regs().cr().modify(|reg| {
|
|
||||||
reg.set_tsel2(trigger.tsel());
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
|
|
||||||
self.check_channel_exists(ch)?;
|
|
||||||
T::regs().swtrigr().write(|reg| {
|
|
||||||
reg.set_swtrig(ch.index(), true);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trigger_all(&mut self) {
|
|
||||||
T::regs().swtrigr().write(|reg| {
|
|
||||||
reg.set_swtrig(Channel::Ch1.index(), true);
|
|
||||||
reg.set_swtrig(Channel::Ch2.index(), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
|
|
||||||
self.check_channel_exists(ch)?;
|
|
||||||
match value {
|
|
||||||
Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
|
||||||
pub trait Instance {
|
|
||||||
fn regs() -> &'static crate::pac::dac::Dac;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
|
||||||
|
|
||||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
|
||||||
|
|
||||||
foreach_peripheral!(
|
|
||||||
(dac, $inst:ident) => {
|
|
||||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
|
||||||
#[cfg(rcc_h7)]
|
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
|
||||||
fn frequency() -> crate::time::Hertz {
|
|
||||||
critical_section::with(|_| unsafe {
|
|
||||||
crate::rcc::get_freqs().apb1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset() {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
|
||||||
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enable() {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable() {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(rcc_h7)]
|
|
||||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
|
||||||
|
|
||||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
|
||||||
fn regs() -> &'static crate::pac::dac::Dac {
|
|
||||||
&crate::pac::$inst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::dac::Instance for peripherals::$inst {}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! impl_dac_pin {
|
|
||||||
($inst:ident, $pin:ident, $ch:expr) => {
|
|
||||||
impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
|
|
||||||
};
|
|
||||||
}
|
|
570
embassy-stm32/src/dac/mod.rs
Normal file
570
embassy-stm32/src/dac/mod.rs
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
|
use crate::pac::dac;
|
||||||
|
use crate::rcc::RccPeripheral;
|
||||||
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Curstom Errors
|
||||||
|
pub enum Error {
|
||||||
|
UnconfiguredChannel,
|
||||||
|
InvalidValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// DAC Channels
|
||||||
|
pub enum Channel {
|
||||||
|
Ch1,
|
||||||
|
Ch2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
const fn index(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Channel::Ch1 => 0,
|
||||||
|
Channel::Ch2 => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Trigger sources for CH1
|
||||||
|
pub enum Ch1Trigger {
|
||||||
|
Tim6,
|
||||||
|
Tim3,
|
||||||
|
Tim7,
|
||||||
|
Tim15,
|
||||||
|
Tim2,
|
||||||
|
Exti9,
|
||||||
|
Software,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ch1Trigger {
|
||||||
|
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||||
|
match self {
|
||||||
|
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||||
|
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
||||||
|
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||||
|
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||||
|
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
||||||
|
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
||||||
|
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Trigger sources for CH2
|
||||||
|
pub enum Ch2Trigger {
|
||||||
|
Tim6,
|
||||||
|
Tim8,
|
||||||
|
Tim7,
|
||||||
|
Tim5,
|
||||||
|
Tim2,
|
||||||
|
Tim4,
|
||||||
|
Exti9,
|
||||||
|
Software,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ch2Trigger {
|
||||||
|
fn tsel(&self) -> dac::vals::Tsel2 {
|
||||||
|
match self {
|
||||||
|
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
||||||
|
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
||||||
|
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
||||||
|
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
||||||
|
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
||||||
|
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
||||||
|
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
||||||
|
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Single 8 or 12 bit value that can be output by the DAC
|
||||||
|
pub enum Value {
|
||||||
|
// 8 bit value
|
||||||
|
Bit8(u8),
|
||||||
|
// 12 bit value stored in a u16, left-aligned
|
||||||
|
Bit12Left(u16),
|
||||||
|
// 12 bit value stored in a u16, right-aligned
|
||||||
|
Bit12Right(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// Array variant of [`Value`]
|
||||||
|
pub enum ValueArray<'a> {
|
||||||
|
// 8 bit values
|
||||||
|
Bit8(&'a [u8]),
|
||||||
|
// 12 bit value stored in a u16, left-aligned
|
||||||
|
Bit12Left(&'a [u16]),
|
||||||
|
// 12 bit values stored in a u16, right-aligned
|
||||||
|
Bit12Right(&'a [u16]),
|
||||||
|
}
|
||||||
|
/// Provide common functions for DAC channels
|
||||||
|
pub trait DacChannel<T: Instance, Tx> {
|
||||||
|
const CHANNEL: Channel;
|
||||||
|
|
||||||
|
/// Enable trigger of the given channel
|
||||||
|
fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_ten(Self::CHANNEL.index(), on);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set mode register of the given channel
|
||||||
|
#[cfg(dac_v2)]
|
||||||
|
fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> {
|
||||||
|
T::regs().mcr().modify(|reg| {
|
||||||
|
reg.set_mode(Self::CHANNEL.index(), val);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set enable register of the given channel
|
||||||
|
fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_en(Self::CHANNEL.index(), on);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the DAC channel `ch`
|
||||||
|
fn enable_channel(&mut self) -> Result<(), Error> {
|
||||||
|
self.set_channel_enable(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the DAC channel `ch`
|
||||||
|
fn disable_channel(&mut self) -> Result<(), Error> {
|
||||||
|
self.set_channel_enable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a software trigger on `ch`
|
||||||
|
fn trigger(&mut self) {
|
||||||
|
T::regs().swtrigr().write(|reg| {
|
||||||
|
reg.set_swtrig(Self::CHANNEL.index(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a value to be output by the DAC on trigger.
|
||||||
|
///
|
||||||
|
/// The `value` is written to the corresponding "data holding register".
|
||||||
|
fn set(&mut self, value: Value) -> Result<(), Error> {
|
||||||
|
match value {
|
||||||
|
Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||||
|
Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||||
|
Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hold two DAC channels
|
||||||
|
///
|
||||||
|
/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
|
||||||
|
///
|
||||||
|
/// # Example for obtaining both DAC channels
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // DMA channels and pins may need to be changed for your controller
|
||||||
|
/// let (dac_ch1, dac_ch2) =
|
||||||
|
/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
|
||||||
|
/// ```
|
||||||
|
pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
|
||||||
|
ch1: DacCh1<'d, T, TxCh1>,
|
||||||
|
ch2: DacCh2<'d, T, TxCh2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DAC CH1
|
||||||
|
///
|
||||||
|
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||||
|
pub struct DacCh1<'d, T: Instance, Tx> {
|
||||||
|
/// To consume T
|
||||||
|
_peri: PeripheralRef<'d, T>,
|
||||||
|
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||||
|
dma: PeripheralRef<'d, Tx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DAC CH2
|
||||||
|
///
|
||||||
|
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
|
||||||
|
pub struct DacCh2<'d, T: Instance, Tx> {
|
||||||
|
/// Instead of PeripheralRef to consume T
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
#[allow(unused)] // For chips whose DMA is not (yet) supported
|
||||||
|
dma: PeripheralRef<'d, Tx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
|
||||||
|
/// Obtain DAC CH1
|
||||||
|
pub fn new(
|
||||||
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
|
dma: impl Peripheral<P = Tx> + 'd,
|
||||||
|
_pin: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(peri, dma);
|
||||||
|
T::enable();
|
||||||
|
T::reset();
|
||||||
|
|
||||||
|
let mut dac = Self { _peri: peri, dma };
|
||||||
|
|
||||||
|
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||||
|
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||||
|
#[cfg(dac_v2)]
|
||||||
|
dac.set_channel_mode(0).unwrap();
|
||||||
|
dac.enable_channel().unwrap();
|
||||||
|
dac.set_trigger_enable(true).unwrap();
|
||||||
|
|
||||||
|
dac
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a new trigger for this channel
|
||||||
|
///
|
||||||
|
/// **Important**: This disables the channel!
|
||||||
|
pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
|
||||||
|
unwrap!(self.disable_channel());
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_tsel1(trigger.tsel());
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write `data` to the DAC CH1 via DMA.
|
||||||
|
///
|
||||||
|
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||||
|
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||||
|
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||||
|
///
|
||||||
|
/// **Important:** Channel 1 has to be configured for the DAC instance!
|
||||||
|
#[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
|
||||||
|
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Tx: DmaCh1<T>,
|
||||||
|
{
|
||||||
|
let channel = Channel::Ch1.index();
|
||||||
|
debug!("Writing to channel {}", channel);
|
||||||
|
|
||||||
|
// Enable DAC and DMA
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
w.set_en(channel, true);
|
||||||
|
w.set_dmaen(channel, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let tx_request = self.dma.request();
|
||||||
|
let dma_channel = &self.dma;
|
||||||
|
|
||||||
|
let tx_options = crate::dma::TransferOptions {
|
||||||
|
circular,
|
||||||
|
half_transfer_ir: false,
|
||||||
|
complete_transfer_ir: !circular,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||||
|
let tx_f = match data {
|
||||||
|
ValueArray::Bit8(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Left(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Right(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tx_f.await;
|
||||||
|
|
||||||
|
// finish dma
|
||||||
|
// TODO: Do we need to check any status registers here?
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
// Disable the DAC peripheral
|
||||||
|
w.set_en(channel, false);
|
||||||
|
// Disable the DMA. TODO: Is this necessary?
|
||||||
|
w.set_dmaen(channel, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
|
||||||
|
/// Obtain DAC CH2
|
||||||
|
pub fn new(
|
||||||
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
|
dma: impl Peripheral<P = Tx> + 'd,
|
||||||
|
_pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(_peri, dma);
|
||||||
|
T::enable();
|
||||||
|
T::reset();
|
||||||
|
|
||||||
|
let mut dac = Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||||
|
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||||
|
#[cfg(dac_v2)]
|
||||||
|
dac.set_channel_mode(0).unwrap();
|
||||||
|
dac.enable_channel().unwrap();
|
||||||
|
dac.set_trigger_enable(true).unwrap();
|
||||||
|
|
||||||
|
dac
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a new trigger for this channel
|
||||||
|
pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
|
||||||
|
unwrap!(self.disable_channel());
|
||||||
|
T::regs().cr().modify(|reg| {
|
||||||
|
reg.set_tsel2(trigger.tsel());
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write `data` to the DAC CH2 via DMA.
|
||||||
|
///
|
||||||
|
/// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
|
||||||
|
/// This will configure a circular DMA transfer that periodically outputs the `data`.
|
||||||
|
/// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
|
||||||
|
///
|
||||||
|
/// **Important:** Channel 2 has to be configured for the DAC instance!
|
||||||
|
#[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
|
||||||
|
pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Tx: DmaCh2<T>,
|
||||||
|
{
|
||||||
|
let channel = Channel::Ch2.index();
|
||||||
|
debug!("Writing to channel {}", channel);
|
||||||
|
|
||||||
|
// Enable DAC and DMA
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
w.set_en(channel, true);
|
||||||
|
w.set_dmaen(channel, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let tx_request = self.dma.request();
|
||||||
|
let dma_channel = &self.dma;
|
||||||
|
|
||||||
|
let tx_options = crate::dma::TransferOptions {
|
||||||
|
circular,
|
||||||
|
half_transfer_ir: false,
|
||||||
|
complete_transfer_ir: !circular,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initiate the correct type of DMA transfer depending on what data is passed
|
||||||
|
let tx_f = match data {
|
||||||
|
ValueArray::Bit8(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr8r(channel).as_ptr() as *mut u8,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Left(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12l(channel).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ValueArray::Bit12Right(buf) => unsafe {
|
||||||
|
crate::dma::Transfer::new_write(
|
||||||
|
dma_channel,
|
||||||
|
tx_request,
|
||||||
|
buf,
|
||||||
|
T::regs().dhr12r(channel).as_ptr() as *mut u16,
|
||||||
|
tx_options,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tx_f.await;
|
||||||
|
|
||||||
|
// finish dma
|
||||||
|
// TODO: Do we need to check any status registers here?
|
||||||
|
T::regs().cr().modify(|w| {
|
||||||
|
// Disable the DAC peripheral
|
||||||
|
w.set_en(channel, false);
|
||||||
|
// Disable the DMA. TODO: Is this necessary?
|
||||||
|
w.set_dmaen(channel, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
|
||||||
|
/// Create a new DAC instance with both channels.
|
||||||
|
///
|
||||||
|
/// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
|
||||||
|
pub fn new(
|
||||||
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
|
dma_ch1: impl Peripheral<P = TxCh1> + 'd,
|
||||||
|
dma_ch2: impl Peripheral<P = TxCh2> + 'd,
|
||||||
|
_pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
|
||||||
|
_pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(peri, dma_ch1, dma_ch2);
|
||||||
|
T::enable();
|
||||||
|
T::reset();
|
||||||
|
|
||||||
|
let mut dac_ch1 = DacCh1 {
|
||||||
|
_peri: peri,
|
||||||
|
dma: dma_ch1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dac_ch2 = DacCh2 {
|
||||||
|
phantom: PhantomData,
|
||||||
|
dma: dma_ch2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure each activated channel. All results can be `unwrap`ed since they
|
||||||
|
// will only error if the channel is not configured (i.e. ch1, ch2 are false)
|
||||||
|
#[cfg(dac_v2)]
|
||||||
|
dac_ch1.set_channel_mode(0).unwrap();
|
||||||
|
dac_ch1.enable_channel().unwrap();
|
||||||
|
dac_ch1.set_trigger_enable(true).unwrap();
|
||||||
|
|
||||||
|
#[cfg(dac_v2)]
|
||||||
|
dac_ch2.set_channel_mode(0).unwrap();
|
||||||
|
dac_ch2.enable_channel().unwrap();
|
||||||
|
dac_ch2.set_trigger_enable(true).unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
ch1: dac_ch1,
|
||||||
|
ch2: dac_ch2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the DAC into CH1 and CH2 for independent use.
|
||||||
|
pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
|
||||||
|
(self.ch1, self.ch2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get mutable reference to CH1
|
||||||
|
pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
|
||||||
|
&mut self.ch1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get mutable reference to CH2
|
||||||
|
pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
|
||||||
|
&mut self.ch2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get reference to CH1
|
||||||
|
pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
|
||||||
|
&self.ch1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get reference to CH2
|
||||||
|
pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
|
||||||
|
&self.ch2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
|
||||||
|
const CHANNEL: Channel = Channel::Ch1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
|
||||||
|
const CHANNEL: Channel = Channel::Ch2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
pub trait Instance {
|
||||||
|
fn regs() -> &'static crate::pac::dac::Dac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||||
|
dma_trait!(DmaCh1, Instance);
|
||||||
|
dma_trait!(DmaCh2, Instance);
|
||||||
|
|
||||||
|
/// Marks a pin that can be used with the DAC
|
||||||
|
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||||
|
|
||||||
|
foreach_peripheral!(
|
||||||
|
(dac, $inst:ident) => {
|
||||||
|
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||||
|
#[cfg(rcc_h7)]
|
||||||
|
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||||
|
fn frequency() -> crate::time::Hertz {
|
||||||
|
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset() {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
|
||||||
|
crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable() {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable() {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rcc_h7)]
|
||||||
|
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||||
|
|
||||||
|
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||||
|
fn regs() -> &'static crate::pac::dac::Dac {
|
||||||
|
&crate::pac::$inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::dac::Instance for peripherals::$inst {}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! impl_dac_pin {
|
||||||
|
($inst:ident, $pin:ident, $ch:expr) => {
|
||||||
|
impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
|
||||||
|
};
|
||||||
|
}
|
@ -21,11 +21,22 @@ use crate::pac::bdma::{regs, vals};
|
|||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct TransferOptions {}
|
pub struct TransferOptions {
|
||||||
|
/// Enable circular DMA
|
||||||
|
pub circular: bool,
|
||||||
|
/// Enable half transfer interrupt
|
||||||
|
pub half_transfer_ir: bool,
|
||||||
|
/// Enable transfer complete interrupt
|
||||||
|
pub complete_transfer_ir: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for TransferOptions {
|
impl Default for TransferOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {}
|
Self {
|
||||||
|
circular: false,
|
||||||
|
half_transfer_ir: false,
|
||||||
|
complete_transfer_ir: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +264,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
mem_len: usize,
|
mem_len: usize,
|
||||||
incr_mem: bool,
|
incr_mem: bool,
|
||||||
data_size: WordSize,
|
data_size: WordSize,
|
||||||
_options: TransferOptions,
|
options: TransferOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ch = channel.regs().ch(channel.num());
|
let ch = channel.regs().ch(channel.num());
|
||||||
|
|
||||||
@ -283,7 +294,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
}
|
}
|
||||||
w.set_dir(dir.into());
|
w.set_dir(dir.into());
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_tcie(true);
|
w.set_tcie(options.complete_transfer_ir);
|
||||||
|
w.set_htie(options.half_transfer_ir);
|
||||||
|
if options.circular {
|
||||||
|
w.set_circ(vals::Circ::ENABLED);
|
||||||
|
debug!("Setting circular mode");
|
||||||
|
} else {
|
||||||
|
w.set_circ(vals::Circ::DISABLED);
|
||||||
|
}
|
||||||
|
w.set_pl(vals::Pl::VERYHIGH);
|
||||||
w.set_en(true);
|
w.set_en(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -310,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
let en = ch.cr().read().en();
|
let en = ch.cr().read().en();
|
||||||
|
let circular = ch.cr().read().circ() == vals::Circ::ENABLED;
|
||||||
let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
|
let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
|
||||||
en && !tcif
|
en && (circular || !tcif)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the total remaining transfers for the channel
|
/// Gets the total remaining transfers for the channel
|
||||||
@ -477,6 +497,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
|
|||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
|
|
||||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||||
|
// If the channel is enabled and transfer is not completed, we need to perform
|
||||||
|
// two separate write access to the CR register to disable the channel.
|
||||||
ch.cr().write(|w| {
|
ch.cr().write(|w| {
|
||||||
w.set_teie(true);
|
w.set_teie(true);
|
||||||
w.set_htie(true);
|
w.set_htie(true);
|
||||||
|
@ -174,7 +174,7 @@ impl<'a> RDesRing<'a> {
|
|||||||
// Receive descriptor unavailable
|
// Receive descriptor unavailable
|
||||||
Rps::SUSPENDED => RunningState::Stopped,
|
Rps::SUSPENDED => RunningState::Stopped,
|
||||||
// Closing receive descriptor
|
// Closing receive descriptor
|
||||||
Rps(0b101) => RunningState::Running,
|
Rps::_RESERVED_5 => RunningState::Running,
|
||||||
// Transferring the receive packet data from receive buffer to host memory
|
// Transferring the receive packet data from receive buffer to host memory
|
||||||
Rps::RUNNINGWRITING => RunningState::Running,
|
Rps::RUNNINGWRITING => RunningState::Running,
|
||||||
_ => RunningState::Unknown,
|
_ => RunningState::Unknown,
|
||||||
|
@ -243,7 +243,7 @@ mod tests {
|
|||||||
for test_run in fn_results {
|
for test_run in fn_results {
|
||||||
let (ckd, bits) = compute_dead_time_value(test_run.value);
|
let (ckd, bits) = compute_dead_time_value(test_run.value);
|
||||||
|
|
||||||
assert_eq!(ckd.0, test_run.ckd.0);
|
assert_eq!(ckd.to_bits(), test_run.ckd.to_bits());
|
||||||
assert_eq!(bits, test_run.bits);
|
assert_eq!(bits, test_run.bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
});
|
});
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
(HSI_FREQ.0 >> div.0, Sw::HSI)
|
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
ClockSrc::HSE(freq) => {
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
@ -157,7 +157,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let mut set_flash_latency_after = false;
|
let mut set_flash_latency_after = false;
|
||||||
FLASH.acr().modify(|w| {
|
FLASH.acr().modify(|w| {
|
||||||
// Is the current flash latency less than what we need at the new SYSCLK?
|
// Is the current flash latency less than what we need at the new SYSCLK?
|
||||||
if w.latency().0 <= target_flash_latency.0 {
|
if w.latency().to_bits() <= target_flash_latency.to_bits() {
|
||||||
// We must increase the number of wait states now
|
// We must increase the number of wait states now
|
||||||
w.set_latency(target_flash_latency)
|
w.set_latency(target_flash_latency)
|
||||||
} else {
|
} else {
|
||||||
@ -171,12 +171,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// > Flash memory.
|
// > Flash memory.
|
||||||
//
|
//
|
||||||
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
|
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
|
||||||
w.set_prften(target_flash_latency.0 > 0);
|
w.set_prften(target_flash_latency.to_bits() > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
if !set_flash_latency_after {
|
if !set_flash_latency_after {
|
||||||
// Spin until the effective flash latency is compatible with the clock change
|
// Spin until the effective flash latency is compatible with the clock change
|
||||||
while FLASH.acr().read().latency().0 < target_flash_latency.0 {}
|
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||||
@ -218,7 +218,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use stm32_metapac::flash::vals::Latency;
|
||||||
|
|
||||||
use super::{set_freqs, Clocks};
|
use super::{set_freqs, Clocks};
|
||||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
@ -85,14 +87,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
||||||
|
|
||||||
FLASH.acr().write(|w| {
|
FLASH.acr().write(|w| {
|
||||||
let latency = if real_sysclk <= 24_000_000 {
|
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||||
0
|
Latency::WS0
|
||||||
} else if real_sysclk <= 48_000_000 {
|
|
||||||
1
|
|
||||||
} else {
|
} else {
|
||||||
2
|
Latency::WS1
|
||||||
};
|
});
|
||||||
w.latency().0 = latency;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
match (config.hse.is_some(), use_hsi48) {
|
match (config.hse.is_some(), use_hsi48) {
|
||||||
@ -134,20 +133,20 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// TODO: Option to use CRS (Clock Recovery)
|
// TODO: Option to use CRS (Clock Recovery)
|
||||||
|
|
||||||
if let Some(pllmul_bits) = pllmul_bits {
|
if let Some(pllmul_bits) = pllmul_bits {
|
||||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits)));
|
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits)));
|
||||||
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
while !RCC.cr().read().pllrdy() {}
|
while !RCC.cr().read().pllrdy() {}
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre(Ppre(ppre_bits));
|
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||||
w.set_hpre(Hpre(hpre_bits));
|
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||||
w.set_sw(Sw::PLL)
|
w.set_sw(Sw::PLL)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre(Ppre(ppre_bits));
|
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||||
w.set_hpre(Hpre(hpre_bits));
|
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||||
|
|
||||||
if config.hse.is_some() {
|
if config.hse.is_some() {
|
||||||
w.set_sw(Sw::HSE);
|
w.set_sw(Sw::HSE);
|
||||||
|
@ -106,11 +106,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// Only needed for stm32f103?
|
// Only needed for stm32f103?
|
||||||
FLASH.acr().write(|w| {
|
FLASH.acr().write(|w| {
|
||||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||||
Latency(0b000)
|
Latency::WS0
|
||||||
} else if real_sysclk <= 48_000_000 {
|
} else if real_sysclk <= 48_000_000 {
|
||||||
Latency(0b001)
|
Latency::WS1
|
||||||
} else {
|
} else {
|
||||||
Latency(0b010)
|
Latency::WS2
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -147,12 +147,13 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
if let Some(pllmul_bits) = pllmul_bits {
|
if let Some(pllmul_bits) = pllmul_bits {
|
||||||
let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
|
let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
|
||||||
RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag)));
|
RCC.cfgr()
|
||||||
|
.modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag)));
|
||||||
|
|
||||||
// enable PLL and wait for it to be ready
|
// enable PLL and wait for it to be ready
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_pllmul(Pllmul(pllmul_bits));
|
w.set_pllmul(Pllmul::from_bits(pllmul_bits));
|
||||||
w.set_pllsrc(Pllsrc(config.hse.is_some() as u8));
|
w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8));
|
||||||
});
|
});
|
||||||
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
@ -161,22 +162,19 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
// Only needed for stm32f103?
|
// Only needed for stm32f103?
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_adcpre(Adcpre(apre_bits));
|
w.set_adcpre(Adcpre::from_bits(apre_bits));
|
||||||
w.set_ppre2(Ppre1(ppre2_bits));
|
w.set_ppre2(Ppre1::from_bits(ppre2_bits));
|
||||||
w.set_ppre1(Ppre1(ppre1_bits));
|
w.set_ppre1(Ppre1::from_bits(ppre1_bits));
|
||||||
w.set_hpre(Hpre(hpre_bits));
|
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||||
#[cfg(not(rcc_f100))]
|
#[cfg(not(rcc_f100))]
|
||||||
w.set_usbpre(Usbpre(usbpre as u8));
|
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||||
w.set_sw(Sw(if pllmul_bits.is_some() {
|
w.set_sw(if pllmul_bits.is_some() {
|
||||||
// PLL
|
Sw::PLL
|
||||||
0b10
|
|
||||||
} else if config.hse.is_some() {
|
} else if config.hse.is_some() {
|
||||||
// HSE
|
Sw::HSE
|
||||||
0b1
|
|
||||||
} else {
|
} else {
|
||||||
// HSI
|
Sw::HSI
|
||||||
0b0
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
|
@ -485,7 +485,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_ppre1(config.apb1_pre.into());
|
w.set_ppre1(config.apb1_pre.into());
|
||||||
w.set_ppre2(config.apb2_pre.into());
|
w.set_ppre2(config.apb2_pre.into());
|
||||||
});
|
});
|
||||||
while RCC.cfgr().read().sws() != sw.0 {}
|
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||||
|
|
||||||
// Turn off HSI to save power if we don't need it
|
// Turn off HSI to save power if we don't need it
|
||||||
if !config.hsi {
|
if !config.hsi {
|
||||||
|
@ -87,7 +87,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
|||||||
|
|
||||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||||
if pllsysclk.is_none() && !pll48clk {
|
if pllsysclk.is_none() && !pll48clk {
|
||||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
|
||||||
|
|
||||||
return PllResults {
|
return PllResults {
|
||||||
use_pll: false,
|
use_pll: false,
|
||||||
@ -141,9 +141,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
|||||||
RCC.pllcfgr().modify(|w| {
|
RCC.pllcfgr().modify(|w| {
|
||||||
w.set_pllm(pllm as u8);
|
w.set_pllm(pllm as u8);
|
||||||
w.set_plln(plln as u16);
|
w.set_plln(plln as u16);
|
||||||
w.set_pllp(Pllp(pllp as u8));
|
w.set_pllp(Pllp::from_bits(pllp as u8));
|
||||||
w.set_pllq(pllq as u8);
|
w.set_pllq(pllq as u8);
|
||||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||||
});
|
});
|
||||||
|
|
||||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||||
@ -323,7 +323,7 @@ fn flash_setup(sysclk: u32) {
|
|||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
FLASH
|
FLASH
|
||||||
.acr()
|
.acr()
|
||||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,8 +440,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre2(Ppre(ppre2_bits));
|
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||||
w.set_ppre1(Ppre(ppre1_bits));
|
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||||
w.set_hpre(hpre_bits);
|
w.set_hpre(hpre_bits);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
|||||||
|
|
||||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||||
if pllsysclk.is_none() && !pll48clk {
|
if pllsysclk.is_none() && !pll48clk {
|
||||||
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
|
||||||
|
|
||||||
return PllResults {
|
return PllResults {
|
||||||
use_pll: false,
|
use_pll: false,
|
||||||
@ -83,9 +83,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
|||||||
RCC.pllcfgr().modify(|w| {
|
RCC.pllcfgr().modify(|w| {
|
||||||
w.set_pllm(pllm as u8);
|
w.set_pllm(pllm as u8);
|
||||||
w.set_plln(plln as u16);
|
w.set_plln(plln as u16);
|
||||||
w.set_pllp(Pllp(pllp as u8));
|
w.set_pllp(Pllp::from_bits(pllp as u8));
|
||||||
w.set_pllq(pllq as u8);
|
w.set_pllq(pllq as u8);
|
||||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||||
});
|
});
|
||||||
|
|
||||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||||
@ -106,7 +106,7 @@ fn flash_setup(sysclk: u32) {
|
|||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
FLASH
|
FLASH
|
||||||
.acr()
|
.acr()
|
||||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,8 +246,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre2(Ppre(ppre2_bits));
|
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||||
w.set_ppre1(Ppre(ppre1_bits));
|
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||||
w.set_hpre(hpre_bits);
|
w.set_hpre(hpre_bits);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
});
|
});
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
(HSI_FREQ.0 >> div.0, Sw::HSI)
|
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
ClockSrc::HSE(freq) => {
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
@ -381,7 +381,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let mut set_flash_latency_after = false;
|
let mut set_flash_latency_after = false;
|
||||||
FLASH.acr().modify(|w| {
|
FLASH.acr().modify(|w| {
|
||||||
// Is the current flash latency less than what we need at the new SYSCLK?
|
// Is the current flash latency less than what we need at the new SYSCLK?
|
||||||
if w.latency().0 <= target_flash_latency.0 {
|
if w.latency().to_bits() <= target_flash_latency.to_bits() {
|
||||||
// We must increase the number of wait states now
|
// We must increase the number of wait states now
|
||||||
w.set_latency(target_flash_latency)
|
w.set_latency(target_flash_latency)
|
||||||
} else {
|
} else {
|
||||||
@ -395,12 +395,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// > Flash memory.
|
// > Flash memory.
|
||||||
//
|
//
|
||||||
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
|
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
|
||||||
w.set_prften(target_flash_latency.0 > 0);
|
w.set_prften(target_flash_latency.to_bits() > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
if !set_flash_latency_after {
|
if !set_flash_latency_after {
|
||||||
// Spin until the effective flash latency is compatible with the clock change
|
// Spin until the effective flash latency is compatible with the clock change
|
||||||
while FLASH.acr().read().latency().0 < target_flash_latency.0 {}
|
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||||
@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
|||||||
use stm32_metapac::FLASH;
|
use stm32_metapac::FLASH;
|
||||||
|
|
||||||
use crate::pac::{PWR, RCC};
|
use crate::pac::{PWR, RCC};
|
||||||
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
|
||||||
|
pub enum Clock48MhzSrc {
|
||||||
|
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||||
|
/// oscillator to comply with the USB specification for oscillator tolerance.
|
||||||
|
Hsi48(Option<CrsConfig>),
|
||||||
|
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
||||||
|
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
||||||
|
/// tolerance.
|
||||||
|
PllQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the sync source for the Clock Recovery System (CRS).
|
||||||
|
pub enum CrsSyncSource {
|
||||||
|
/// Use an external GPIO to sync the CRS.
|
||||||
|
Gpio,
|
||||||
|
/// Use the Low Speed External oscillator to sync the CRS.
|
||||||
|
Lse,
|
||||||
|
/// Use the USB SOF to sync the CRS.
|
||||||
|
Usb,
|
||||||
|
}
|
||||||
|
|
||||||
/// Clocks configutation
|
/// Clocks configutation
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mux: ClockSrc,
|
pub mux: ClockSrc,
|
||||||
@ -326,6 +348,14 @@ pub struct Config {
|
|||||||
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
||||||
/// MUST turn on the PLLR output.
|
/// MUST turn on the PLLR output.
|
||||||
pub pll: Option<Pll>,
|
pub pll: Option<Pll>,
|
||||||
|
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||||
|
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||||
|
pub struct CrsConfig {
|
||||||
|
/// Sync source for the CRS.
|
||||||
|
pub sync_src: CrsSyncSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -338,6 +368,7 @@ impl Default for Config {
|
|||||||
apb2_pre: APBPrescaler::NotDivided,
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
low_power_run: false,
|
low_power_run: false,
|
||||||
pll: None,
|
pll: None,
|
||||||
|
clock_48mhz_src: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
assert!(pll_freq.is_some());
|
assert!(pll_freq.is_some());
|
||||||
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
||||||
|
|
||||||
let freq = pll_freq.unwrap().pll_r.unwrap().0;
|
let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
|
||||||
|
|
||||||
assert!(freq <= 170_000_000);
|
assert!(freq <= 170_000_000);
|
||||||
|
|
||||||
@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Setup the 48 MHz clock if needed
|
||||||
|
if let Some(clock_48mhz_src) = config.clock_48mhz_src {
|
||||||
|
let source = match clock_48mhz_src {
|
||||||
|
Clock48MhzSrc::PllQ => {
|
||||||
|
// Make sure the PLLQ is enabled and running at 48Mhz
|
||||||
|
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
||||||
|
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
||||||
|
|
||||||
|
crate::pac::rcc::vals::Clk48sel::PLLQCLK
|
||||||
|
}
|
||||||
|
Clock48MhzSrc::Hsi48(crs_config) => {
|
||||||
|
// Enable HSI48
|
||||||
|
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||||
|
// Wait for HSI48 to turn on
|
||||||
|
while RCC.crrcr().read().hsi48rdy() == false {}
|
||||||
|
|
||||||
|
// Enable and setup CRS if needed
|
||||||
|
if let Some(crs_config) = crs_config {
|
||||||
|
crate::peripherals::CRS::enable();
|
||||||
|
|
||||||
|
let sync_src = match crs_config.sync_src {
|
||||||
|
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
|
||||||
|
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
|
||||||
|
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::pac::CRS.cfgr().modify(|w| {
|
||||||
|
w.set_syncsrc(sync_src);
|
||||||
|
});
|
||||||
|
|
||||||
|
// These are the correct settings for standard USB operation. If other settings
|
||||||
|
// are needed there will need to be additional config options for the CRS.
|
||||||
|
crate::pac::CRS.cr().modify(|w| {
|
||||||
|
w.set_autotrimen(true);
|
||||||
|
w.set_cen(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
crate::pac::rcc::vals::Clk48sel::HSI48
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||||
|
}
|
||||||
|
|
||||||
if config.low_power_run {
|
if config.low_power_run {
|
||||||
assert!(sys_clk <= 2_000_000);
|
assert!(sys_clk <= 2_000_000);
|
||||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||||
|
@ -601,22 +601,22 @@ pub(crate) unsafe fn init(mut config: Config) {
|
|||||||
|
|
||||||
// Core Prescaler / AHB Prescaler / APB3 Prescaler
|
// Core Prescaler / AHB Prescaler / APB3 Prescaler
|
||||||
RCC.d1cfgr().modify(|w| {
|
RCC.d1cfgr().modify(|w| {
|
||||||
w.set_d1cpre(Hpre(d1cpre_bits));
|
w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
|
||||||
w.set_d1ppre(Dppre(ppre3_bits));
|
w.set_d1ppre(Dppre::from_bits(ppre3_bits));
|
||||||
w.set_hpre(hpre_bits)
|
w.set_hpre(hpre_bits)
|
||||||
});
|
});
|
||||||
// Ensure core prescaler value is valid before future lower
|
// Ensure core prescaler value is valid before future lower
|
||||||
// core voltage
|
// core voltage
|
||||||
while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {}
|
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
|
||||||
|
|
||||||
// APB1 / APB2 Prescaler
|
// APB1 / APB2 Prescaler
|
||||||
RCC.d2cfgr().modify(|w| {
|
RCC.d2cfgr().modify(|w| {
|
||||||
w.set_d2ppre1(Dppre(ppre1_bits));
|
w.set_d2ppre1(Dppre::from_bits(ppre1_bits));
|
||||||
w.set_d2ppre2(Dppre(ppre2_bits));
|
w.set_d2ppre2(Dppre::from_bits(ppre2_bits));
|
||||||
});
|
});
|
||||||
|
|
||||||
// APB4 Prescaler
|
// APB4 Prescaler
|
||||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits)));
|
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits)));
|
||||||
|
|
||||||
// Peripheral Clock (per_ck)
|
// Peripheral Clock (per_ck)
|
||||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||||
@ -640,7 +640,7 @@ pub(crate) unsafe fn init(mut config: Config) {
|
|||||||
_ => Sw::HSI,
|
_ => Sw::HSI,
|
||||||
};
|
};
|
||||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||||
while RCC.cfgr().read().sws() != sw.0 {}
|
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||||
|
|
||||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||||
assert!(RCC.cr().read().csirdy());
|
assert!(RCC.cr().read().csirdy());
|
||||||
@ -806,7 +806,8 @@ mod pll {
|
|||||||
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
||||||
let vco_ck = ref_x_ck * pll_x_n;
|
let vco_ck = ref_x_ck * pll_x_n;
|
||||||
|
|
||||||
RCC.plldivr(plln).modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8)));
|
RCC.plldivr(plln)
|
||||||
|
.modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
|
||||||
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
||||||
|
|
||||||
// Calulate additional output dividers
|
// Calulate additional output dividers
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||||
use crate::pac::RCC;
|
use crate::pac::RCC;
|
||||||
#[cfg(crs)]
|
#[cfg(crs)]
|
||||||
use crate::pac::{CRS, SYSCFG};
|
use crate::pac::{crs, CRS, SYSCFG};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
AHBPrescaler::NotDivided => sys_clk,
|
AHBPrescaler::NotDivided => sys_clk,
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Hpre = pre.into();
|
let pre: Hpre = pre.into();
|
||||||
let pre = 1 << (pre.0 as u32 - 7);
|
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||||
sys_clk / pre
|
sys_clk / pre
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -302,7 +302,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
CRS.cfgr().write(|w|
|
CRS.cfgr().write(|w|
|
||||||
|
|
||||||
// Select LSE as synchronization source
|
// Select LSE as synchronization source
|
||||||
w.set_syncsrc(0b01));
|
w.set_syncsrc(crs::vals::Syncsrc::LSE));
|
||||||
CRS.cr().modify(|w| {
|
CRS.cr().modify(|w| {
|
||||||
w.set_autotrimen(true);
|
w.set_autotrimen(true);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
|
@ -294,7 +294,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
AHBPrescaler::NotDivided => sys_clk,
|
AHBPrescaler::NotDivided => sys_clk,
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Hpre = pre.into();
|
let pre: Hpre = pre.into();
|
||||||
let pre = 1 << (pre.0 as u32 - 7);
|
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||||
sys_clk / pre
|
sys_clk / pre
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -303,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
|
@ -635,7 +635,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
AHBPrescaler::NotDivided => sys_clk,
|
AHBPrescaler::NotDivided => sys_clk,
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Hpre = pre.into();
|
let pre: Hpre = pre.into();
|
||||||
let pre = 1 << (pre.0 as u32 - 7);
|
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||||
sys_clk / pre
|
sys_clk / pre
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -644,7 +644,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
@ -654,7 +654,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
AHBPrescaler::NotDivided => sys_clk,
|
AHBPrescaler::NotDivided => sys_clk,
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Hpre = pre.into();
|
let pre: Hpre = pre.into();
|
||||||
let pre = 1 << (pre.0 as u32 - 7);
|
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||||
sys_clk / pre
|
sys_clk / pre
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -470,7 +470,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
@ -480,7 +480,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||||
pre => {
|
pre => {
|
||||||
let pre: Ppre = pre.into();
|
let pre: Ppre = pre.into();
|
||||||
let pre: u8 = 1 << (pre.0 - 3);
|
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||||
let freq = ahb_freq / pre as u32;
|
let freq = ahb_freq / pre as u32;
|
||||||
(freq, freq * 2)
|
(freq, freq * 2)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ pub enum PllM {
|
|||||||
|
|
||||||
impl Into<Pllm> for PllM {
|
impl Into<Pllm> for PllM {
|
||||||
fn into(self) -> Pllm {
|
fn into(self) -> Pllm {
|
||||||
Pllm(self as u8)
|
Pllm::from_bits(self as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
|||||||
#[cfg(rtc_v2wb)]
|
#[cfg(rtc_v2wb)]
|
||||||
let rtcsel = reg.rtcsel();
|
let rtcsel = reg.rtcsel();
|
||||||
#[cfg(not(rtc_v2wb))]
|
#[cfg(not(rtc_v2wb))]
|
||||||
let rtcsel = reg.rtcsel().0;
|
let rtcsel = reg.rtcsel().to_bits();
|
||||||
|
|
||||||
if !reg.rtcen() || rtcsel != clock_config {
|
if !reg.rtcen() || rtcsel != clock_config {
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||||
@ -54,7 +54,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
|||||||
|
|
||||||
// Select RTC source
|
// Select RTC source
|
||||||
#[cfg(not(rtc_v2wb))]
|
#[cfg(not(rtc_v2wb))]
|
||||||
w.set_rtcsel(Rtcsel(clock_config));
|
w.set_rtcsel(Rtcsel::from_bits(clock_config));
|
||||||
#[cfg(rtc_v2wb)]
|
#[cfg(rtc_v2wb)]
|
||||||
w.set_rtcsel(clock_config);
|
w.set_rtcsel(clock_config);
|
||||||
w.set_rtcen(true);
|
w.set_rtcen(true);
|
||||||
|
@ -26,7 +26,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
|
|||||||
|
|
||||||
let config_rtcsel = rtc_config.clock_config as u8;
|
let config_rtcsel = rtc_config.clock_config as u8;
|
||||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||||
let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel);
|
let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel);
|
||||||
|
|
||||||
if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
|
if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
|
||||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||||
|
@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp
|
|||||||
fifo_threshold: Some(crate::dma::FifoThreshold::Full),
|
fifo_threshold: Some(crate::dma::FifoThreshold::Full),
|
||||||
};
|
};
|
||||||
#[cfg(all(sdmmc_v1, not(dma)))]
|
#[cfg(all(sdmmc_v1, not(dma)))]
|
||||||
const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {};
|
const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {
|
||||||
|
circular: false,
|
||||||
|
half_transfer_ir: false,
|
||||||
|
complete_transfer_ir: true,
|
||||||
|
};
|
||||||
|
|
||||||
/// SDMMC configuration
|
/// SDMMC configuration
|
||||||
///
|
///
|
||||||
|
@ -650,7 +650,7 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
|
|||||||
_ => 0b111,
|
_ => 0b111,
|
||||||
};
|
};
|
||||||
|
|
||||||
Br(val)
|
Br::from_bits(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RegsExt {
|
trait RegsExt {
|
||||||
@ -772,7 +772,7 @@ fn set_rxdmaen(regs: Regs, val: bool) {
|
|||||||
|
|
||||||
fn finish_dma(regs: Regs) {
|
fn finish_dma(regs: Regs) {
|
||||||
#[cfg(spi_v2)]
|
#[cfg(spi_v2)]
|
||||||
while regs.sr().read().ftlvl() > 0 {}
|
while regs.sr().read().ftlvl().to_bits() > 0 {}
|
||||||
|
|
||||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||||
while !regs.sr().read().txc() {}
|
while !regs.sr().read().txc() {}
|
||||||
|
@ -869,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
|
|||||||
_ => vals::Ps::EVEN,
|
_ => vals::Ps::EVEN,
|
||||||
});
|
});
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
w.set_over8(vals::Over8(over8 as _));
|
w.set_over8(vals::Over8::from_bits(over8 as _));
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
|
@ -97,8 +97,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
}
|
}
|
||||||
epr.set_dtog_rx(false);
|
epr.set_dtog_rx(false);
|
||||||
epr.set_dtog_tx(false);
|
epr.set_dtog_tx(false);
|
||||||
epr.set_stat_rx(Stat(0));
|
epr.set_stat_rx(Stat::from_bits(0));
|
||||||
epr.set_stat_tx(Stat(0));
|
epr.set_stat_tx(Stat::from_bits(0));
|
||||||
epr.set_ctr_rx(!epr.ctr_rx());
|
epr.set_ctr_rx(!epr.ctr_rx());
|
||||||
epr.set_ctr_tx(!epr.ctr_tx());
|
epr.set_ctr_tx(!epr.ctr_tx());
|
||||||
regs.epr(index).write_value(epr);
|
regs.epr(index).write_value(epr);
|
||||||
@ -143,8 +143,8 @@ fn invariant(mut r: regs::Epr) -> regs::Epr {
|
|||||||
r.set_ctr_tx(true); // don't clear
|
r.set_ctr_tx(true); // don't clear
|
||||||
r.set_dtog_rx(false); // don't toggle
|
r.set_dtog_rx(false); // don't toggle
|
||||||
r.set_dtog_tx(false); // don't toggle
|
r.set_dtog_tx(false); // don't toggle
|
||||||
r.set_stat_rx(Stat(0));
|
r.set_stat_rx(Stat::from_bits(0));
|
||||||
r.set_stat_tx(Stat(0));
|
r.set_stat_tx(Stat::from_bits(0));
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,56 +480,57 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| {
|
||||||
BUS_WAKER.register(cx.waker());
|
BUS_WAKER.register(cx.waker());
|
||||||
|
|
||||||
if self.inited {
|
// TODO: implement VBUS detection.
|
||||||
let regs = T::regs();
|
if !self.inited {
|
||||||
|
|
||||||
if IRQ_RESUME.load(Ordering::Acquire) {
|
|
||||||
IRQ_RESUME.store(false, Ordering::Relaxed);
|
|
||||||
return Poll::Ready(Event::Resume);
|
|
||||||
}
|
|
||||||
|
|
||||||
if IRQ_RESET.load(Ordering::Acquire) {
|
|
||||||
IRQ_RESET.store(false, Ordering::Relaxed);
|
|
||||||
|
|
||||||
trace!("RESET");
|
|
||||||
regs.daddr().write(|w| {
|
|
||||||
w.set_ef(true);
|
|
||||||
w.set_add(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
regs.epr(0).write(|w| {
|
|
||||||
w.set_ep_type(EpType::CONTROL);
|
|
||||||
w.set_stat_rx(Stat::NAK);
|
|
||||||
w.set_stat_tx(Stat::NAK);
|
|
||||||
});
|
|
||||||
|
|
||||||
for i in 1..EP_COUNT {
|
|
||||||
regs.epr(i).write(|w| {
|
|
||||||
w.set_ea(i as _);
|
|
||||||
w.set_ep_type(self.ep_types[i - 1]);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for w in &EP_IN_WAKERS {
|
|
||||||
w.wake()
|
|
||||||
}
|
|
||||||
for w in &EP_OUT_WAKERS {
|
|
||||||
w.wake()
|
|
||||||
}
|
|
||||||
|
|
||||||
return Poll::Ready(Event::Reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if IRQ_SUSPEND.load(Ordering::Acquire) {
|
|
||||||
IRQ_SUSPEND.store(false, Ordering::Relaxed);
|
|
||||||
return Poll::Ready(Event::Suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
|
||||||
} else {
|
|
||||||
self.inited = true;
|
self.inited = true;
|
||||||
return Poll::Ready(Event::PowerDetected);
|
return Poll::Ready(Event::PowerDetected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
|
||||||
|
if IRQ_RESUME.load(Ordering::Acquire) {
|
||||||
|
IRQ_RESUME.store(false, Ordering::Relaxed);
|
||||||
|
return Poll::Ready(Event::Resume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if IRQ_RESET.load(Ordering::Acquire) {
|
||||||
|
IRQ_RESET.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
trace!("RESET");
|
||||||
|
regs.daddr().write(|w| {
|
||||||
|
w.set_ef(true);
|
||||||
|
w.set_add(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
regs.epr(0).write(|w| {
|
||||||
|
w.set_ep_type(EpType::CONTROL);
|
||||||
|
w.set_stat_rx(Stat::NAK);
|
||||||
|
w.set_stat_tx(Stat::NAK);
|
||||||
|
});
|
||||||
|
|
||||||
|
for i in 1..EP_COUNT {
|
||||||
|
regs.epr(i).write(|w| {
|
||||||
|
w.set_ea(i as _);
|
||||||
|
w.set_ep_type(self.ep_types[i - 1]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for w in &EP_IN_WAKERS {
|
||||||
|
w.wake()
|
||||||
|
}
|
||||||
|
for w in &EP_OUT_WAKERS {
|
||||||
|
w.wake()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Poll::Ready(Event::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if IRQ_SUSPEND.load(Ordering::Acquire) {
|
||||||
|
IRQ_SUSPEND.store(false, Ordering::Relaxed);
|
||||||
|
return Poll::Ready(Event::Suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -550,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
true => Stat::STALL,
|
true => Stat::STALL,
|
||||||
};
|
};
|
||||||
let mut w = invariant(r);
|
let mut w = invariant(r);
|
||||||
w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
|
w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
|
||||||
reg.write_value(w);
|
reg.write_value(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
true => Stat::STALL,
|
true => Stat::STALL,
|
||||||
};
|
};
|
||||||
let mut w = invariant(r);
|
let mut w = invariant(r);
|
||||||
w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
|
w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
|
||||||
reg.write_value(w);
|
reg.write_value(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,7 +606,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let mut w = invariant(r);
|
let mut w = invariant(r);
|
||||||
w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
|
w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
|
||||||
reg.write_value(w);
|
reg.write_value(w);
|
||||||
}
|
}
|
||||||
EP_IN_WAKERS[ep_addr.index()].wake();
|
EP_IN_WAKERS[ep_addr.index()].wake();
|
||||||
@ -621,7 +622,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let mut w = invariant(r);
|
let mut w = invariant(r);
|
||||||
w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
|
w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
|
||||||
reg.write_value(w);
|
reg.write_value(w);
|
||||||
}
|
}
|
||||||
EP_OUT_WAKERS[ep_addr.index()].wake();
|
EP_OUT_WAKERS[ep_addr.index()].wake();
|
||||||
@ -762,8 +763,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
|
|||||||
regs.epr(index).write(|w| {
|
regs.epr(index).write(|w| {
|
||||||
w.set_ep_type(convert_type(self.info.ep_type));
|
w.set_ep_type(convert_type(self.info.ep_type));
|
||||||
w.set_ea(self.info.addr.index() as _);
|
w.set_ea(self.info.addr.index() as _);
|
||||||
w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||||
w.set_stat_tx(Stat(0));
|
w.set_stat_tx(Stat::from_bits(0));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
});
|
});
|
||||||
@ -804,8 +805,8 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
regs.epr(index).write(|w| {
|
regs.epr(index).write(|w| {
|
||||||
w.set_ep_type(convert_type(self.info.ep_type));
|
w.set_ep_type(convert_type(self.info.ep_type));
|
||||||
w.set_ea(self.info.addr.index() as _);
|
w.set_ea(self.info.addr.index() as _);
|
||||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||||
w.set_stat_rx(Stat(0));
|
w.set_stat_rx(Stat::from_bits(0));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
});
|
});
|
||||||
@ -868,19 +869,19 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
let mut stat_tx = 0;
|
let mut stat_tx = 0;
|
||||||
if first {
|
if first {
|
||||||
// change NAK -> VALID
|
// change NAK -> VALID
|
||||||
stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0;
|
stat_rx ^= Stat::NAK.to_bits() ^ Stat::VALID.to_bits();
|
||||||
stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
stat_tx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
|
||||||
}
|
}
|
||||||
if last {
|
if last {
|
||||||
// change STALL -> VALID
|
// change STALL -> VALID
|
||||||
stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0;
|
stat_tx ^= Stat::STALL.to_bits() ^ Stat::NAK.to_bits();
|
||||||
}
|
}
|
||||||
// Note: if this is the first AND last transfer, the above effectively
|
// Note: if this is the first AND last transfer, the above effectively
|
||||||
// changes stat_tx like NAK -> NAK, so noop.
|
// changes stat_tx like NAK -> NAK, so noop.
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_rx(Stat(stat_rx));
|
w.set_stat_rx(Stat::from_bits(stat_rx));
|
||||||
w.set_stat_tx(Stat(stat_tx));
|
w.set_stat_tx(Stat::from_bits(stat_tx));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
});
|
});
|
||||||
@ -907,11 +908,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
|
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_rx(Stat(match last {
|
w.set_stat_rx(Stat::from_bits(match last {
|
||||||
// If last, set STAT_RX=STALL.
|
// If last, set STAT_RX=STALL.
|
||||||
true => Stat::NAK.0 ^ Stat::STALL.0,
|
true => Stat::NAK.to_bits() ^ Stat::STALL.to_bits(),
|
||||||
// Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
|
// Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
|
||||||
false => Stat::NAK.0 ^ Stat::VALID.0,
|
false => Stat::NAK.to_bits() ^ Stat::VALID.to_bits(),
|
||||||
}));
|
}));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
@ -936,17 +937,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
let mut stat_rx = 0;
|
let mut stat_rx = 0;
|
||||||
if first {
|
if first {
|
||||||
// change NAK -> STALL
|
// change NAK -> STALL
|
||||||
stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0;
|
stat_rx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
|
||||||
}
|
}
|
||||||
if last {
|
if last {
|
||||||
// change STALL -> VALID
|
// change STALL -> VALID
|
||||||
stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0;
|
stat_rx ^= Stat::STALL.to_bits() ^ Stat::VALID.to_bits();
|
||||||
}
|
}
|
||||||
// Note: if this is the first AND last transfer, the above effectively
|
// Note: if this is the first AND last transfer, the above effectively
|
||||||
// does a change of NAK -> VALID.
|
// does a change of NAK -> VALID.
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_rx(Stat(stat_rx));
|
w.set_stat_rx(Stat::from_bits(stat_rx));
|
||||||
w.set_ep_kind(last); // set OUT_STATUS if last.
|
w.set_ep_kind(last); // set OUT_STATUS if last.
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
@ -976,7 +977,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
|
w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
|
||||||
w.set_ep_kind(last); // set OUT_STATUS if last.
|
w.set_ep_kind(last); // set OUT_STATUS if last.
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
@ -997,8 +998,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
let epr = regs.epr(0).read();
|
let epr = regs.epr(0).read();
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
|
||||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
|
w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::VALID.to_bits()));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
});
|
});
|
||||||
@ -1028,8 +1029,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
|||||||
let epr = regs.epr(0).read();
|
let epr = regs.epr(0).read();
|
||||||
regs.epr(0).write(|w| {
|
regs.epr(0).write(|w| {
|
||||||
w.set_ep_type(EpType::CONTROL);
|
w.set_ep_type(EpType::CONTROL);
|
||||||
w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
|
w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
|
||||||
w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
|
w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::STALL.to_bits()));
|
||||||
w.set_ctr_rx(true); // don't clear
|
w.set_ctr_rx(true); // don't clear
|
||||||
w.set_ctr_tx(true); // don't clear
|
w.set_ctr_tx(true); // don't clear
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering};
|
|||||||
use embassy_hal_common::{into_ref, Peripheral};
|
use embassy_hal_common::{into_ref, Peripheral};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embassy_usb_driver::{
|
use embassy_usb_driver::{
|
||||||
self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut,
|
self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo,
|
||||||
EndpointType, Event, Unsupported,
|
EndpointOut, EndpointType, Event, Unsupported,
|
||||||
};
|
};
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
let state = T::state();
|
let state = T::state();
|
||||||
|
|
||||||
let ints = r.gintsts().read();
|
let ints = r.gintsts().read();
|
||||||
if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() {
|
if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() {
|
||||||
// Mask interrupts and notify `Bus` to process them
|
// Mask interrupts and notify `Bus` to process them
|
||||||
r.gintmsk().write(|_| {});
|
r.gintmsk().write(|_| {});
|
||||||
T::state().bus_waker.wake();
|
T::state().bus_waker.wake();
|
||||||
@ -97,7 +97,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
vals::Pktstsd::SETUP_DATA_DONE => {
|
vals::Pktstsd::SETUP_DATA_DONE => {
|
||||||
trace!("SETUP_DATA_DONE ep={}", ep_num);
|
trace!("SETUP_DATA_DONE ep={}", ep_num);
|
||||||
}
|
}
|
||||||
x => trace!("unknown PKTSTS: {}", x.0),
|
x => trace!("unknown PKTSTS: {}", x.to_bits()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.ep_in_wakers[ep_num].wake();
|
state.ep_in_wakers[ep_num].wake();
|
||||||
trace!("in ep={} irq val={:b}", ep_num, ep_ints.0);
|
trace!("in ep={} irq val={:08x}", ep_num, ep_ints.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ep_mask >>= 1;
|
ep_mask >>= 1;
|
||||||
@ -144,7 +144,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
// // clear all
|
// // clear all
|
||||||
// r.doepint(ep_num).write_value(ep_ints);
|
// r.doepint(ep_num).write_value(ep_ints);
|
||||||
// state.ep_out_wakers[ep_num].wake();
|
// state.ep_out_wakers[ep_num].wake();
|
||||||
// trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0);
|
// trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// ep_mask >>= 1;
|
// ep_mask >>= 1;
|
||||||
@ -256,7 +256,34 @@ struct EndpointData {
|
|||||||
fifo_size_words: u16,
|
fifo_size_words: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
/// Enable VBUS detection.
|
||||||
|
///
|
||||||
|
/// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly.
|
||||||
|
/// This is done by checkihg whether there is 5V on the VBUS pin or not.
|
||||||
|
///
|
||||||
|
/// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional.
|
||||||
|
/// (if there's no power in VBUS your device would be off anyway, so it's fine to always assume
|
||||||
|
/// there's power in VBUS, i.e. the USB cable is always plugged in.)
|
||||||
|
///
|
||||||
|
/// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and
|
||||||
|
/// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true.
|
||||||
|
///
|
||||||
|
/// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a
|
||||||
|
/// voltage divider. See ST application note AN4879 and the reference manual for more details.
|
||||||
|
pub vbus_detection: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { vbus_detection: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Driver<'d, T: Instance> {
|
pub struct Driver<'d, T: Instance> {
|
||||||
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
||||||
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
||||||
ep_out_buffer: &'d mut [u8],
|
ep_out_buffer: &'d mut [u8],
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(dp, dm);
|
into_ref!(dp, dm);
|
||||||
|
|
||||||
@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: [None; MAX_EP_COUNT],
|
ep_in: [None; MAX_EP_COUNT],
|
||||||
ep_out: [None; MAX_EP_COUNT],
|
ep_out: [None; MAX_EP_COUNT],
|
||||||
@ -318,6 +347,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
|
ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
|
||||||
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
||||||
ep_out_buffer: &'d mut [u8],
|
ep_out_buffer: &'d mut [u8],
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
|
assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
|
||||||
|
|
||||||
@ -327,6 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: [None; MAX_EP_COUNT],
|
ep_in: [None; MAX_EP_COUNT],
|
||||||
ep_out: [None; MAX_EP_COUNT],
|
ep_out: [None; MAX_EP_COUNT],
|
||||||
@ -464,11 +495,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
|||||||
|
|
||||||
(
|
(
|
||||||
Bus {
|
Bus {
|
||||||
|
config: self.config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: self.ep_in,
|
ep_in: self.ep_in,
|
||||||
ep_out: self.ep_out,
|
ep_out: self.ep_out,
|
||||||
phy_type: self.phy_type,
|
phy_type: self.phy_type,
|
||||||
enabled: false,
|
inited: false,
|
||||||
},
|
},
|
||||||
ControlPipe {
|
ControlPipe {
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
@ -481,11 +513,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bus<'d, T: Instance> {
|
pub struct Bus<'d, T: Instance> {
|
||||||
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
phy_type: PhyType,
|
phy_type: PhyType,
|
||||||
enabled: bool,
|
inited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Bus<'d, T> {
|
impl<'d, T: Instance> Bus<'d, T> {
|
||||||
@ -498,282 +531,14 @@ impl<'d, T: Instance> Bus<'d, T> {
|
|||||||
w.set_iepint(true);
|
w.set_iepint(true);
|
||||||
w.set_oepint(true);
|
w.set_oepint(true);
|
||||||
w.set_rxflvlm(true);
|
w.set_rxflvlm(true);
|
||||||
|
w.set_srqim(true);
|
||||||
|
w.set_otgint(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Bus<'d, T> {
|
impl<'d, T: Instance> Bus<'d, T> {
|
||||||
fn init_fifo(&mut self) {
|
fn init(&mut self) {
|
||||||
trace!("init_fifo");
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
|
|
||||||
// Configure RX fifo size. All endpoints share the same FIFO area.
|
|
||||||
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
|
|
||||||
trace!("configuring rx fifo size={}", rx_fifo_size_words);
|
|
||||||
|
|
||||||
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
|
|
||||||
|
|
||||||
// Configure TX (USB in direction) fifo size for each endpoint
|
|
||||||
let mut fifo_top = rx_fifo_size_words;
|
|
||||||
for i in 0..T::ENDPOINT_COUNT {
|
|
||||||
if let Some(ep) = self.ep_in[i] {
|
|
||||||
trace!(
|
|
||||||
"configuring tx fifo ep={}, offset={}, size={}",
|
|
||||||
i,
|
|
||||||
fifo_top,
|
|
||||||
ep.fifo_size_words
|
|
||||||
);
|
|
||||||
|
|
||||||
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
|
|
||||||
|
|
||||||
dieptxf.write(|w| {
|
|
||||||
w.set_fd(ep.fifo_size_words);
|
|
||||||
w.set_sa(fifo_top);
|
|
||||||
});
|
|
||||||
|
|
||||||
fifo_top += ep.fifo_size_words;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
fifo_top <= T::FIFO_DEPTH_WORDS,
|
|
||||||
"FIFO allocations exceeded maximum capacity"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_endpoints(&mut self) {
|
|
||||||
trace!("configure_endpoints");
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
|
|
||||||
// Configure IN endpoints
|
|
||||||
for (index, ep) in self.ep_in.iter().enumerate() {
|
|
||||||
if let Some(ep) = ep {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
r.diepctl(index).write(|w| {
|
|
||||||
if index == 0 {
|
|
||||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
|
||||||
} else {
|
|
||||||
w.set_mpsiz(ep.max_packet_size);
|
|
||||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
|
||||||
w.set_sd0pid_sevnfrm(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure OUT endpoints
|
|
||||||
for (index, ep) in self.ep_out.iter().enumerate() {
|
|
||||||
if let Some(ep) = ep {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
r.doepctl(index).write(|w| {
|
|
||||||
if index == 0 {
|
|
||||||
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
|
||||||
} else {
|
|
||||||
w.set_mpsiz(ep.max_packet_size);
|
|
||||||
w.set_eptyp(to_eptyp(ep.ep_type));
|
|
||||||
w.set_sd0pid_sevnfrm(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
r.doeptsiz(index).modify(|w| {
|
|
||||||
w.set_xfrsiz(ep.max_packet_size as _);
|
|
||||||
if index == 0 {
|
|
||||||
w.set_rxdpid_stupcnt(1);
|
|
||||||
} else {
|
|
||||||
w.set_pktcnt(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable IRQs for allocated endpoints
|
|
||||||
r.daintmsk().modify(|w| {
|
|
||||||
w.set_iepm(ep_irq_mask(&self.ep_in));
|
|
||||||
// OUT interrupts not used, handled in RXFLVL
|
|
||||||
// w.set_oepm(ep_irq_mask(&self.ep_out));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable(&mut self) {
|
|
||||||
T::Interrupt::disable();
|
|
||||||
|
|
||||||
<T as RccPeripheral>::disable();
|
|
||||||
|
|
||||||
#[cfg(stm32l4)]
|
|
||||||
crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
|
|
||||||
// Cannot disable PWR, because other peripherals might be using it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
|
||||||
async fn poll(&mut self) -> Event {
|
|
||||||
poll_fn(move |cx| {
|
|
||||||
// TODO: implement VBUS detection
|
|
||||||
if !self.enabled {
|
|
||||||
return Poll::Ready(Event::PowerDetected);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
|
|
||||||
T::state().bus_waker.register(cx.waker());
|
|
||||||
|
|
||||||
let ints = r.gintsts().read();
|
|
||||||
if ints.usbrst() {
|
|
||||||
trace!("reset");
|
|
||||||
|
|
||||||
self.init_fifo();
|
|
||||||
self.configure_endpoints();
|
|
||||||
|
|
||||||
// Reset address
|
|
||||||
critical_section::with(|_| {
|
|
||||||
r.dcfg().modify(|w| {
|
|
||||||
w.set_dad(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
r.gintsts().write(|w| w.set_usbrst(true)); // clear
|
|
||||||
Self::restore_irqs();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ints.enumdne() {
|
|
||||||
trace!("enumdne");
|
|
||||||
|
|
||||||
let speed = r.dsts().read().enumspd();
|
|
||||||
trace!(" speed={}", speed.0);
|
|
||||||
|
|
||||||
r.gusbcfg().modify(|w| {
|
|
||||||
w.set_trdt(calculate_trdt(speed, T::frequency()));
|
|
||||||
});
|
|
||||||
|
|
||||||
r.gintsts().write(|w| w.set_enumdne(true)); // clear
|
|
||||||
Self::restore_irqs();
|
|
||||||
|
|
||||||
return Poll::Ready(Event::Reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ints.usbsusp() {
|
|
||||||
trace!("suspend");
|
|
||||||
r.gintsts().write(|w| w.set_usbsusp(true)); // clear
|
|
||||||
Self::restore_irqs();
|
|
||||||
return Poll::Ready(Event::Suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ints.wkupint() {
|
|
||||||
trace!("resume");
|
|
||||||
r.gintsts().write(|w| w.set_wkupint(true)); // clear
|
|
||||||
Self::restore_irqs();
|
|
||||||
return Poll::Ready(Event::Resume);
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
|
|
||||||
trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
|
||||||
"endpoint_set_stalled index {} out of range",
|
|
||||||
ep_addr.index()
|
|
||||||
);
|
|
||||||
|
|
||||||
let regs = T::regs();
|
|
||||||
match ep_addr.direction() {
|
|
||||||
Direction::Out => {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
regs.doepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_stall(stalled);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
|
||||||
}
|
|
||||||
Direction::In => {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
regs.diepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_stall(stalled);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
|
|
||||||
assert!(
|
|
||||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
|
||||||
"endpoint_is_stalled index {} out of range",
|
|
||||||
ep_addr.index()
|
|
||||||
);
|
|
||||||
|
|
||||||
let regs = T::regs();
|
|
||||||
|
|
||||||
match ep_addr.direction() {
|
|
||||||
Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),
|
|
||||||
Direction::In => regs.diepctl(ep_addr.index()).read().stall(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
|
|
||||||
trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
ep_addr.index() < T::ENDPOINT_COUNT,
|
|
||||||
"endpoint_set_enabled index {} out of range",
|
|
||||||
ep_addr.index()
|
|
||||||
);
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
match ep_addr.direction() {
|
|
||||||
Direction::Out => {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
// cancel transfer if active
|
|
||||||
if !enabled && r.doepctl(ep_addr.index()).read().epena() {
|
|
||||||
r.doepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_snak(true);
|
|
||||||
w.set_epdis(true);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
r.doepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_usbaep(enabled);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wake `Endpoint::wait_enabled()`
|
|
||||||
T::state().ep_out_wakers[ep_addr.index()].wake();
|
|
||||||
}
|
|
||||||
Direction::In => {
|
|
||||||
critical_section::with(|_| {
|
|
||||||
// cancel transfer if active
|
|
||||||
if !enabled && r.diepctl(ep_addr.index()).read().epena() {
|
|
||||||
r.diepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_snak(true);
|
|
||||||
w.set_epdis(true);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
r.diepctl(ep_addr.index()).modify(|w| {
|
|
||||||
w.set_usbaep(enabled);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wake `Endpoint::wait_enabled()`
|
|
||||||
T::state().ep_in_wakers[ep_addr.index()].wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn enable(&mut self) {
|
|
||||||
trace!("enable");
|
|
||||||
|
|
||||||
#[cfg(stm32l4)]
|
#[cfg(stm32l4)]
|
||||||
{
|
{
|
||||||
crate::peripherals::PWR::enable();
|
crate::peripherals::PWR::enable();
|
||||||
@ -908,9 +673,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
|||||||
|
|
||||||
// F429-like chips have the GCCFG.NOVBUSSENS bit
|
// F429-like chips have the GCCFG.NOVBUSSENS bit
|
||||||
r.gccfg_v1().modify(|w| {
|
r.gccfg_v1().modify(|w| {
|
||||||
w.set_novbussens(true);
|
w.set_novbussens(!self.config.vbus_detection);
|
||||||
w.set_vbusasen(false);
|
w.set_vbusasen(false);
|
||||||
w.set_vbusbsen(false);
|
w.set_vbusbsen(self.config.vbus_detection);
|
||||||
w.set_sofouten(false);
|
w.set_sofouten(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -923,12 +688,12 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
r.gccfg_v2().modify(|w| {
|
r.gccfg_v2().modify(|w| {
|
||||||
w.set_vbden(false);
|
w.set_vbden(self.config.vbus_detection);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Force B-peripheral session
|
// Force B-peripheral session
|
||||||
r.gotgctl().modify(|w| {
|
r.gotgctl().modify(|w| {
|
||||||
w.set_bvaloen(true);
|
w.set_bvaloen(!self.config.vbus_detection);
|
||||||
w.set_bvaloval(true);
|
w.set_bvaloval(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -960,16 +725,352 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
|||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
r.dctl().write(|w| w.set_sdis(false));
|
r.dctl().write(|w| w.set_sdis(false));
|
||||||
|
}
|
||||||
|
|
||||||
self.enabled = true;
|
fn init_fifo(&mut self) {
|
||||||
|
trace!("init_fifo");
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Configure RX fifo size. All endpoints share the same FIFO area.
|
||||||
|
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
|
||||||
|
trace!("configuring rx fifo size={}", rx_fifo_size_words);
|
||||||
|
|
||||||
|
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
|
||||||
|
|
||||||
|
// Configure TX (USB in direction) fifo size for each endpoint
|
||||||
|
let mut fifo_top = rx_fifo_size_words;
|
||||||
|
for i in 0..T::ENDPOINT_COUNT {
|
||||||
|
if let Some(ep) = self.ep_in[i] {
|
||||||
|
trace!(
|
||||||
|
"configuring tx fifo ep={}, offset={}, size={}",
|
||||||
|
i,
|
||||||
|
fifo_top,
|
||||||
|
ep.fifo_size_words
|
||||||
|
);
|
||||||
|
|
||||||
|
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
|
||||||
|
|
||||||
|
dieptxf.write(|w| {
|
||||||
|
w.set_fd(ep.fifo_size_words);
|
||||||
|
w.set_sa(fifo_top);
|
||||||
|
});
|
||||||
|
|
||||||
|
fifo_top += ep.fifo_size_words;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
fifo_top <= T::FIFO_DEPTH_WORDS,
|
||||||
|
"FIFO allocations exceeded maximum capacity"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Flush fifos
|
||||||
|
r.grstctl().write(|w| {
|
||||||
|
w.set_rxfflsh(true);
|
||||||
|
w.set_txfflsh(true);
|
||||||
|
w.set_txfnum(0x10);
|
||||||
|
});
|
||||||
|
loop {
|
||||||
|
let x = r.grstctl().read();
|
||||||
|
if !x.rxfflsh() && !x.txfflsh() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_endpoints(&mut self) {
|
||||||
|
trace!("configure_endpoints");
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Configure IN endpoints
|
||||||
|
for (index, ep) in self.ep_in.iter().enumerate() {
|
||||||
|
if let Some(ep) = ep {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
r.diepctl(index).write(|w| {
|
||||||
|
if index == 0 {
|
||||||
|
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||||
|
} else {
|
||||||
|
w.set_mpsiz(ep.max_packet_size);
|
||||||
|
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||||
|
w.set_sd0pid_sevnfrm(true);
|
||||||
|
w.set_txfnum(index as _);
|
||||||
|
w.set_snak(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure OUT endpoints
|
||||||
|
for (index, ep) in self.ep_out.iter().enumerate() {
|
||||||
|
if let Some(ep) = ep {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
r.doepctl(index).write(|w| {
|
||||||
|
if index == 0 {
|
||||||
|
w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
|
||||||
|
} else {
|
||||||
|
w.set_mpsiz(ep.max_packet_size);
|
||||||
|
w.set_eptyp(to_eptyp(ep.ep_type));
|
||||||
|
w.set_sd0pid_sevnfrm(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
r.doeptsiz(index).modify(|w| {
|
||||||
|
w.set_xfrsiz(ep.max_packet_size as _);
|
||||||
|
if index == 0 {
|
||||||
|
w.set_rxdpid_stupcnt(1);
|
||||||
|
} else {
|
||||||
|
w.set_pktcnt(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable IRQs for allocated endpoints
|
||||||
|
r.daintmsk().modify(|w| {
|
||||||
|
w.set_iepm(ep_irq_mask(&self.ep_in));
|
||||||
|
// OUT interrupts not used, handled in RXFLVL
|
||||||
|
// w.set_oepm(ep_irq_mask(&self.ep_out));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_all_endpoints(&mut self) {
|
||||||
|
for i in 0..T::ENDPOINT_COUNT {
|
||||||
|
self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false);
|
||||||
|
self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable(&mut self) {
|
||||||
|
T::Interrupt::disable();
|
||||||
|
|
||||||
|
<T as RccPeripheral>::disable();
|
||||||
|
|
||||||
|
#[cfg(stm32l4)]
|
||||||
|
crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
|
||||||
|
// Cannot disable PWR, because other peripherals might be using it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||||
|
async fn poll(&mut self) -> Event {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
if !self.inited {
|
||||||
|
self.init();
|
||||||
|
self.inited = true;
|
||||||
|
|
||||||
|
// If no vbus detection, just return a single PowerDetected event at startup.
|
||||||
|
if !self.config.vbus_detection {
|
||||||
|
return Poll::Ready(Event::PowerDetected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
T::state().bus_waker.register(cx.waker());
|
||||||
|
|
||||||
|
let ints = r.gintsts().read();
|
||||||
|
|
||||||
|
if ints.srqint() {
|
||||||
|
trace!("vbus detected");
|
||||||
|
|
||||||
|
r.gintsts().write(|w| w.set_srqint(true)); // clear
|
||||||
|
Self::restore_irqs();
|
||||||
|
|
||||||
|
if self.config.vbus_detection {
|
||||||
|
return Poll::Ready(Event::PowerDetected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.otgint() {
|
||||||
|
let otgints = r.gotgint().read();
|
||||||
|
r.gotgint().write_value(otgints); // clear all
|
||||||
|
Self::restore_irqs();
|
||||||
|
|
||||||
|
if otgints.sedet() {
|
||||||
|
trace!("vbus removed");
|
||||||
|
if self.config.vbus_detection {
|
||||||
|
self.disable_all_endpoints();
|
||||||
|
return Poll::Ready(Event::PowerRemoved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.usbrst() {
|
||||||
|
trace!("reset");
|
||||||
|
|
||||||
|
self.init_fifo();
|
||||||
|
self.configure_endpoints();
|
||||||
|
|
||||||
|
// Reset address
|
||||||
|
critical_section::with(|_| {
|
||||||
|
r.dcfg().modify(|w| {
|
||||||
|
w.set_dad(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
r.gintsts().write(|w| w.set_usbrst(true)); // clear
|
||||||
|
Self::restore_irqs();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.enumdne() {
|
||||||
|
trace!("enumdne");
|
||||||
|
|
||||||
|
let speed = r.dsts().read().enumspd();
|
||||||
|
trace!(" speed={}", speed.to_bits());
|
||||||
|
|
||||||
|
r.gusbcfg().modify(|w| {
|
||||||
|
w.set_trdt(calculate_trdt(speed, T::frequency()));
|
||||||
|
});
|
||||||
|
|
||||||
|
r.gintsts().write(|w| w.set_enumdne(true)); // clear
|
||||||
|
Self::restore_irqs();
|
||||||
|
|
||||||
|
return Poll::Ready(Event::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.usbsusp() {
|
||||||
|
trace!("suspend");
|
||||||
|
r.gintsts().write(|w| w.set_usbsusp(true)); // clear
|
||||||
|
Self::restore_irqs();
|
||||||
|
return Poll::Ready(Event::Suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.wkupint() {
|
||||||
|
trace!("resume");
|
||||||
|
r.gintsts().write(|w| w.set_wkupint(true)); // clear
|
||||||
|
Self::restore_irqs();
|
||||||
|
return Poll::Ready(Event::Resume);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
|
||||||
|
trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||||
|
"endpoint_set_stalled index {} out of range",
|
||||||
|
ep_addr.index()
|
||||||
|
);
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
match ep_addr.direction() {
|
||||||
|
Direction::Out => {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
regs.doepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_stall(stalled);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||||
|
}
|
||||||
|
Direction::In => {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
regs.diepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_stall(stalled);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
|
||||||
|
assert!(
|
||||||
|
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||||
|
"endpoint_is_stalled index {} out of range",
|
||||||
|
ep_addr.index()
|
||||||
|
);
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
|
||||||
|
match ep_addr.direction() {
|
||||||
|
Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),
|
||||||
|
Direction::In => regs.diepctl(ep_addr.index()).read().stall(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
|
||||||
|
trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
ep_addr.index() < T::ENDPOINT_COUNT,
|
||||||
|
"endpoint_set_enabled index {} out of range",
|
||||||
|
ep_addr.index()
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
match ep_addr.direction() {
|
||||||
|
Direction::Out => {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
// cancel transfer if active
|
||||||
|
if !enabled && r.doepctl(ep_addr.index()).read().epena() {
|
||||||
|
r.doepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_snak(true);
|
||||||
|
w.set_epdis(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
r.doepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_usbaep(enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Flush tx fifo
|
||||||
|
r.grstctl().write(|w| {
|
||||||
|
w.set_txfflsh(true);
|
||||||
|
w.set_txfnum(ep_addr.index() as _);
|
||||||
|
});
|
||||||
|
loop {
|
||||||
|
let x = r.grstctl().read();
|
||||||
|
if !x.txfflsh() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wake `Endpoint::wait_enabled()`
|
||||||
|
T::state().ep_out_wakers[ep_addr.index()].wake();
|
||||||
|
}
|
||||||
|
Direction::In => {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
// cancel transfer if active
|
||||||
|
if !enabled && r.diepctl(ep_addr.index()).read().epena() {
|
||||||
|
r.diepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_snak(true); // set NAK
|
||||||
|
w.set_epdis(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
r.diepctl(ep_addr.index()).modify(|w| {
|
||||||
|
w.set_usbaep(enabled);
|
||||||
|
w.set_cnak(enabled); // clear NAK that might've been set by SNAK above.
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wake `Endpoint::wait_enabled()`
|
||||||
|
T::state().ep_in_wakers[ep_addr.index()].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enable(&mut self) {
|
||||||
|
trace!("enable");
|
||||||
|
// TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn disable(&mut self) {
|
async fn disable(&mut self) {
|
||||||
trace!("disable");
|
trace!("disable");
|
||||||
|
|
||||||
Bus::disable(self);
|
// TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||||
|
//Bus::disable(self);
|
||||||
self.enabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
|
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
|
||||||
@ -1112,11 +1213,16 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
state.ep_in_wakers[index].register(cx.waker());
|
state.ep_in_wakers[index].register(cx.waker());
|
||||||
|
|
||||||
let diepctl = r.diepctl(index).read();
|
let diepctl = r.diepctl(index).read();
|
||||||
|
let dtxfsts = r.dtxfsts(index).read();
|
||||||
|
info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0);
|
||||||
if !diepctl.usbaep() {
|
if !diepctl.usbaep() {
|
||||||
|
trace!("write ep={:?} wait for prev: error disabled", self.info.addr);
|
||||||
Poll::Ready(Err(EndpointError::Disabled))
|
Poll::Ready(Err(EndpointError::Disabled))
|
||||||
} else if !diepctl.epena() {
|
} else if !diepctl.epena() {
|
||||||
|
trace!("write ep={:?} wait for prev: ready", self.info.addr);
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
|
trace!("write ep={:?} wait for prev: pending", self.info.addr);
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1141,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
|
trace!("write ep={:?} wait for fifo: ready", self.info.addr);
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -49,7 +49,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
|||||||
|
|
||||||
let wdg = T::regs();
|
let wdg = T::regs();
|
||||||
wdg.kr().write(|w| w.set_key(Key::ENABLE));
|
wdg.kr().write(|w| w.set_key(Key::ENABLE));
|
||||||
wdg.pr().write(|w| w.set_pr(Pr(pr)));
|
wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr)));
|
||||||
wdg.rlr().write(|w| w.set_rl(rl));
|
wdg.rlr().write(|w| w.set_rl(rl));
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -282,7 +282,7 @@ where
|
|||||||
/// returns the amount of bytes written.
|
/// returns the amount of bytes written.
|
||||||
///
|
///
|
||||||
/// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full,
|
/// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full,
|
||||||
/// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that
|
/// this method will wait until it isn't. See [`try_write`](Self::try_write) for a variant that
|
||||||
/// returns an error instead of waiting.
|
/// returns an error instead of waiting.
|
||||||
///
|
///
|
||||||
/// It is not guaranteed that all bytes in the buffer are written, even if there's enough
|
/// It is not guaranteed that all bytes in the buffer are written, even if there's enough
|
||||||
@ -319,7 +319,7 @@ where
|
|||||||
/// returns the amount of bytes read.
|
/// returns the amount of bytes read.
|
||||||
///
|
///
|
||||||
/// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty,
|
/// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty,
|
||||||
/// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that
|
/// this method will wait until it isn't. See [`try_read`](Self::try_read) for a variant that
|
||||||
/// returns an error instead of waiting.
|
/// returns an error instead of waiting.
|
||||||
///
|
///
|
||||||
/// It is not guaranteed that all bytes in the buffer are read, even if there's enough
|
/// It is not guaranteed that all bytes in the buffer are read, even if there's enough
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class.
|
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.
|
||||||
|
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::LinkState;
|
use embassy_net_driver_channel::driver::LinkState;
|
||||||
@ -79,7 +80,7 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
|
|||||||
pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
|
pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
|
||||||
|
|
||||||
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
||||||
/// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net).
|
/// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net).
|
||||||
pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
||||||
self,
|
self,
|
||||||
state: &'d mut State<MTU, N_RX, N_TX>,
|
state: &'d mut State<MTU, N_RX, N_TX>,
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
|
//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
|
||||||
//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
|
//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
|
||||||
//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
|
//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
|
||||||
//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
|
//! This is due to regex spaghetti: <https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417>
|
||||||
//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
|
//! and this nonsense in the linux kernel: <https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757>
|
||||||
|
|
||||||
use core::intrinsics::copy_nonoverlapping;
|
use core::intrinsics::copy_nonoverlapping;
|
||||||
use core::mem::{size_of, MaybeUninit};
|
use core::mem::{size_of, MaybeUninit};
|
||||||
|
@ -23,7 +23,7 @@ mod config {
|
|||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
pub use crate::builder::{Builder, Config};
|
pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder};
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use crate::control::*;
|
use crate::control::*;
|
||||||
use crate::descriptor::*;
|
use crate::descriptor::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list`
|
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip nRF52840_xxAA"
|
runner = "probe-rs run --chip nRF52840_xxAA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -3,7 +3,7 @@ build-std = ["core"]
|
|||||||
build-std-features = ["panic_immediate_abort"]
|
build-std-features = ["panic_immediate_abort"]
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
runner = "probe-rs-cli run --chip RP2040"
|
runner = "probe-rs run --chip RP2040"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F303VCTx"
|
runner = "probe-rs run --chip STM32F303VCTx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F767ZITx"
|
runner = "probe-rs run --chip STM32F767ZITx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32H743ZITx"
|
runner = "probe-rs run --chip STM32H743ZITx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
probe-rs-cli erase --chip STM32H743ZITx
|
probe-rs erase --chip STM32H743ZITx
|
||||||
mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x
|
mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x
|
||||||
cp memory-bl.x ../../bootloader/stm32/memory.x
|
cp memory-bl.x ../../bootloader/stm32/memory.x
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32L072CZTx"
|
runner = "probe-rs run --chip STM32L072CZTx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32L151CBxxA"
|
runner = "probe-rs run --chip STM32L151CBxxA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7m-none-eabi"
|
target = "thumbv7m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32L475VG"
|
runner = "probe-rs run --chip STM32L475VG"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32WLE5JCIx"
|
runner = "probe-rs run --chip STM32WLE5JCIx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -4,7 +4,7 @@ build-std-features = ["panic_immediate_abort"]
|
|||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
#runner = "./fruitrunner"
|
#runner = "./fruitrunner"
|
||||||
runner = "probe-rs-cli run --chip nrf52840_xxAA"
|
runner = "probe-rs run --chip nrf52840_xxAA"
|
||||||
|
|
||||||
rustflags = [
|
rustflags = [
|
||||||
# Code-size optimizations.
|
# Code-size optimizations.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
runner = "probe-rs-cli run --chip RP2040"
|
runner = "probe-rs run --chip RP2040"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list`
|
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip nRF52840_xxAA"
|
runner = "probe-rs run --chip nRF52840_xxAA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list`
|
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip nRF52840_xxAA"
|
runner = "probe-rs run --chip nRF52840_xxAA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list`
|
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip nRF52840_xxAA"
|
runner = "probe-rs run --chip nRF52840_xxAA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_nrf::init(Default::default());
|
let p = embassy_nrf::init(Default::default());
|
||||||
info!("Hello NVMC!");
|
info!("Hello NVMC!");
|
||||||
|
|
||||||
// probe-rs-cli run breaks without this, I'm not sure why.
|
// probe-rs run breaks without this, I'm not sure why.
|
||||||
Timer::after(Duration::from_secs(1)).await;
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
|
||||||
let mut f = Nvmc::new(p.NVMC);
|
let mut f = Nvmc::new(p.NVMC);
|
||||||
|
@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.timeout_ticks = 32768 * 3; // 3 seconds
|
config.timeout_ticks = 32768 * 3; // 3 seconds
|
||||||
|
|
||||||
// This is needed for `probe-rs-cli run` to be able to catch the panic message
|
// This is needed for `probe-rs run` to be able to catch the panic message
|
||||||
// in the WDT interrupt. The core resets 2 ticks after firing the interrupt.
|
// in the WDT interrupt. The core resets 2 ticks after firing the interrupt.
|
||||||
config.run_during_debug_halt = false;
|
config.run_during_debug_halt = false;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace nRF5340_xxAA with your chip as listed in `probe-rs-cli chip list`
|
# replace nRF5340_xxAA with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip nRF5340_xxAA"
|
runner = "probe-rs run --chip nRF5340_xxAA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv8m.main-none-eabihf"
|
target = "thumbv8m.main-none-eabihf"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
runner = "probe-rs-cli run --chip RP2040"
|
runner = "probe-rs run --chip RP2040"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||||
|
@ -42,8 +42,8 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// To make flashing faster for development, you may want to flash the firmwares independently
|
// To make flashing faster for development, you may want to flash the firmwares independently
|
||||||
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
||||||
// probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
// probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
||||||
// probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
// probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
||||||
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
||||||
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// To make flashing faster for development, you may want to flash the firmwares independently
|
// To make flashing faster for development, you may want to flash the firmwares independently
|
||||||
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
||||||
// probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
// probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
||||||
// probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
// probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
||||||
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
||||||
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
||||||
|
|
||||||
|
@ -39,8 +39,8 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// To make flashing faster for development, you may want to flash the firmwares independently
|
// To make flashing faster for development, you may want to flash the firmwares independently
|
||||||
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
||||||
// probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
// probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
||||||
// probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
// probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
||||||
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
||||||
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// To make flashing faster for development, you may want to flash the firmwares independently
|
// To make flashing faster for development, you may want to flash the firmwares independently
|
||||||
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
// at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
|
||||||
// probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
// probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
|
||||||
// probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
// probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
|
||||||
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
//let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
|
||||||
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
//let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
|
||||||
|
|
||||||
|
23
examples/std/README.md
Normal file
23
examples/std/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
## Running the `embassy-net` examples
|
||||||
|
|
||||||
|
First, create the tap0 interface. You only need to do this once.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ip tuntap add name tap0 mode tap user $USER
|
||||||
|
sudo ip link set tap0 up
|
||||||
|
sudo ip addr add 192.168.69.100/24 dev tap0
|
||||||
|
sudo ip -6 addr add fe80::100/64 dev tap0
|
||||||
|
sudo ip -6 addr add fdaa::100/64 dev tap0
|
||||||
|
sudo ip -6 route add fe80::/64 dev tap0
|
||||||
|
sudo ip -6 route add fdaa::/64 dev tap0
|
||||||
|
```
|
||||||
|
|
||||||
|
Second, have something listening there. For example `nc -l 8000`
|
||||||
|
|
||||||
|
Then run the example located in the `examples` folder:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd $EMBASSY_ROOT/examples/std/
|
||||||
|
cargo run --bin net -- --static-ip
|
||||||
|
```
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx"
|
runner = "probe-rs run --speed 100 --chip STM32c031c6tx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[target.thumbv6m-none-eabi]
|
[target.thumbv6m-none-eabi]
|
||||||
runner = 'probe-rs-cli run --chip STM32F091RCTX'
|
runner = 'probe-rs run --chip STM32F091RCTX'
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F103C8 with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F103C8 with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F103C8"
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7m-none-eabi"
|
target = "thumbv7m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F207ZGTx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F207ZGTx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F207ZGTx"
|
runner = "probe-rs run --chip STM32F207ZGTx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7m-none-eabi"
|
target = "thumbv7m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F303ZETx"
|
runner = "probe-rs run --chip STM32F303ZETx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F429ZITx"
|
runner = "probe-rs run --chip STM32F429ZITx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::dac::{Channel, Dac, Value};
|
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Hello World, dude!");
|
info!("Hello World, dude!");
|
||||||
|
|
||||||
let mut dac = Dac::new_1ch(p.DAC, p.PA4);
|
let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v))));
|
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||||
unwrap!(dac.trigger(Channel::Ch1));
|
dac.trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,9 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let ep_out_buffer = &mut make_static!([0; 256])[..];
|
let ep_out_buffer = &mut make_static!([0; 256])[..];
|
||||||
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer);
|
let mut config = embassy_stm32::usb_otg::Config::default();
|
||||||
|
config.vbus_detection = true;
|
||||||
|
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config);
|
||||||
|
|
||||||
// Create embassy-usb Config
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
let mut ep_out_buffer = [0u8; 256];
|
||||||
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer);
|
let mut config = embassy_stm32::usb_otg::Config::default();
|
||||||
|
config.vbus_detection = true;
|
||||||
|
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
||||||
|
|
||||||
// Create embassy-usb Config
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32F767ZITx"
|
runner = "probe-rs run --chip STM32F767ZITx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
|
@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
let mut ep_out_buffer = [0u8; 256];
|
||||||
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer);
|
let mut config = embassy_stm32::usb_otg::Config::default();
|
||||||
|
config.vbus_detection = true;
|
||||||
|
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
||||||
|
|
||||||
// Create embassy-usb Config
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32G071RBTx"
|
runner = "probe-rs run --chip STM32G071RBTx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32G484VETx"
|
runner = "probe-rs run --chip STM32G484VETx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
use defmt::{panic, *};
|
use defmt::{panic, *};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc};
|
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc};
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::usb::{self, Driver, Instance};
|
use embassy_stm32::usb::{self, Driver, Instance};
|
||||||
use embassy_stm32::{bind_interrupts, pac, peripherals, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, Config};
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
use embassy_usb::driver::EndpointError;
|
use embassy_usb::driver::EndpointError;
|
||||||
use embassy_usb::Builder;
|
use embassy_usb::Builder;
|
||||||
@ -22,23 +22,35 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
|
||||||
|
// Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
|
||||||
|
const USE_HSI48: bool = true;
|
||||||
|
|
||||||
|
let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) };
|
||||||
|
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PllSrc::HSE(Hertz(8000000)),
|
source: PllSrc::HSE(Hertz(8_000_000)),
|
||||||
prediv_m: PllM::Div2,
|
prediv_m: PllM::Div2,
|
||||||
mul_n: PllN::Mul72,
|
mul_n: PllN::Mul72,
|
||||||
div_p: None,
|
div_p: None,
|
||||||
// USB and CAN at 48 MHz
|
div_q: pllq_div,
|
||||||
div_q: Some(PllQ::Div6),
|
|
||||||
// Main system clock at 144 MHz
|
// Main system clock at 144 MHz
|
||||||
div_r: Some(PllR::Div2),
|
div_r: Some(PllR::Div2),
|
||||||
});
|
});
|
||||||
|
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL;
|
||||||
|
|
||||||
let p = embassy_stm32::init(config);
|
if USE_HSI48 {
|
||||||
info!("Hello World!");
|
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
|
||||||
|
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig {
|
||||||
|
sync_src: CrsSyncSource::Usb,
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
|
||||||
|
}
|
||||||
|
|
||||||
pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10));
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[target.thumbv8m.main-none-eabihf]
|
[target.thumbv8m.main-none-eabihf]
|
||||||
runner = 'probe-rs-cli run --chip STM32H563ZITx'
|
runner = 'probe-rs run --chip STM32H563ZITx'
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv8m.main-none-eabihf"
|
target = "thumbv8m.main-none-eabihf"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[target.thumbv7em-none-eabihf]
|
[target.thumbv7em-none-eabihf]
|
||||||
runner = 'probe-rs-cli run --chip STM32H743ZITx'
|
runner = 'probe-rs run --chip STM32H743ZITx'
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_stm32::dac::{Channel, Dac, Value};
|
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
use embassy_stm32::time::mhz;
|
use embassy_stm32::time::mhz;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -19,12 +20,12 @@ fn main() -> ! {
|
|||||||
config.rcc.pll1.q_ck = Some(mhz(100));
|
config.rcc.pll1.q_ck = Some(mhz(100));
|
||||||
let p = embassy_stm32::init(config);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
let mut dac = Dac::new_1ch(p.DAC1, p.PA4);
|
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for v in 0..=255 {
|
for v in 0..=255 {
|
||||||
unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v))));
|
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||||
unwrap!(dac.trigger(Channel::Ch1));
|
dac.trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
let mut ep_out_buffer = [0u8; 256];
|
||||||
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer);
|
let mut config = embassy_stm32::usb_otg::Config::default();
|
||||||
|
config.vbus_detection = true;
|
||||||
|
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
||||||
|
|
||||||
// Create embassy-usb Config
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32L053R8Tx"
|
runner = "probe-rs run --chip STM32L053R8Tx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-rs-cli chip list`
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
runner = "probe-rs-cli run --chip STM32L151CBxxA"
|
runner = "probe-rs run --chip STM32L151CBxxA"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7m-none-eabi"
|
target = "thumbv7m-none-eabi"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
|
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
|
||||||
#runner = "probe-rs-cli run --chip STM32L475VGT6"
|
#runner = "probe-rs run --chip STM32L475VGT6"
|
||||||
#runner = "probe-rs-cli run --chip STM32L475VG"
|
#runner = "probe-rs run --chip STM32L475VG"
|
||||||
runner = "probe-rs-cli run --chip STM32L4S5VI"
|
runner = "probe-rs run --chip STM32L4S5VI"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user